aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs')
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs494
1 files changed, 165 insertions, 329 deletions
diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
index 7097d0f7..868e0172 100644
--- a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs
@@ -1,6 +1,5 @@
using ChocolArm64.State;
using Ryujinx.HLE.Logging;
-using System;
using static Ryujinx.HLE.HOS.ErrorCode;
@@ -8,64 +7,122 @@ namespace Ryujinx.HLE.HOS.Kernel
{
partial class SvcHandler
{
- private const int MutexHasListenersMask = 0x40000000;
-
- private void SvcArbitrateLock(AThreadState ThreadState)
+ private void SvcWaitSynchronization(AThreadState ThreadState)
{
- int OwnerThreadHandle = (int)ThreadState.X0;
- long MutexAddress = (long)ThreadState.X1;
- int WaitThreadHandle = (int)ThreadState.X2;
+ long HandlesPtr = (long)ThreadState.X1;
+ int HandlesCount = (int)ThreadState.X2;
+ long Timeout = (long)ThreadState.X3;
Device.Log.PrintDebug(LogClass.KernelSvc,
- "OwnerThreadHandle = 0x" + OwnerThreadHandle.ToString("x8") + ", " +
- "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
- "WaitThreadHandle = 0x" + WaitThreadHandle .ToString("x8"));
+ "HandlesPtr = 0x" + HandlesPtr .ToString("x16") + ", " +
+ "HandlesCount = 0x" + HandlesCount.ToString("x8") + ", " +
+ "Timeout = 0x" + Timeout .ToString("x16"));
- if (IsPointingInsideKernel(MutexAddress))
+ if ((uint)HandlesCount > 0x40)
{
- Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
-
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.CountOutOfRange);
return;
}
- if (IsAddressNotWordAligned(MutexAddress))
+ KSynchronizationObject[] SyncObjs = new KSynchronizationObject[HandlesCount];
+
+ for (int Index = 0; Index < HandlesCount; Index++)
{
- Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
+ int Handle = Memory.ReadInt32(HandlesPtr + Index * 4);
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
+ KSynchronizationObject SyncObj = Process.HandleTable.GetData<KSynchronizationObject>(Handle);
- return;
+ SyncObjs[Index] = SyncObj;
+ }
+
+ int HndIndex = (int)ThreadState.X1;
+
+ ulong High = ThreadState.X1 & (0xffffffffUL << 32);
+
+ long Result = System.Synchronization.WaitFor(SyncObjs, Timeout, ref HndIndex);
+
+ if (Result != 0)
+ {
+ if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout) ||
+ Result == MakeError(ErrorModule.Kernel, KernelErr.Cancelled))
+ {
+ Device.Log.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ }
+ else
+ {
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ }
}
- KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
+ ThreadState.X0 = (ulong)Result;
+ ThreadState.X1 = (uint)HndIndex | High;
+ }
+
+ private void SvcCancelSynchronization(AThreadState ThreadState)
+ {
+ int ThreadHandle = (int)ThreadState.X0;
+
+ Device.Log.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8"));
- if (OwnerThread == null)
+ KThread Thread = Process.HandleTable.GetData<KThread>(ThreadHandle);
+
+ if (Thread == null)
{
- Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid owner thread handle 0x{OwnerThreadHandle:x8}!");
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
return;
}
- KThread WaitThread = Process.HandleTable.GetData<KThread>(WaitThreadHandle);
+ Thread.CancelSynchronization();
+
+ ThreadState.X0 = 0;
+ }
- if (WaitThread == null)
+ private void SvcArbitrateLock(AThreadState ThreadState)
+ {
+ int OwnerHandle = (int)ThreadState.X0;
+ long MutexAddress = (long)ThreadState.X1;
+ int RequesterHandle = (int)ThreadState.X2;
+
+ Device.Log.PrintDebug(LogClass.KernelSvc,
+ "OwnerHandle = 0x" + OwnerHandle .ToString("x8") + ", " +
+ "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
+ "RequesterHandle = 0x" + RequesterHandle.ToString("x8"));
+
+ if (IsPointingInsideKernel(MutexAddress))
{
- Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid requesting thread handle 0x{WaitThreadHandle:x8}!");
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!");
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
+ if (IsAddressNotWordAligned(MutexAddress))
+ {
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!");
- MutexLock(CurrThread, WaitThread, OwnerThreadHandle, WaitThreadHandle, MutexAddress);
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
- ThreadState.X0 = 0;
+ return;
+ }
+
+ long Result = System.AddressArbiter.ArbitrateLock(
+ Process,
+ Memory,
+ OwnerHandle,
+ MutexAddress,
+ RequesterHandle);
+
+ if (Result != 0)
+ {
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ }
+
+ ThreadState.X0 = (ulong)Result;
}
private void SvcArbitrateUnlock(AThreadState ThreadState)
@@ -92,9 +149,14 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- MutexUnlock(Process.GetThread(ThreadState.Tpidr), MutexAddress);
+ long Result = System.AddressArbiter.ArbitrateUnlock(Memory, MutexAddress);
- ThreadState.X0 = 0;
+ if (Result != 0)
+ {
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ }
+
+ ThreadState.X0 = (ulong)Result;
}
private void SvcWaitProcessWideKeyAtomic(AThreadState ThreadState)
@@ -102,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel
long MutexAddress = (long)ThreadState.X0;
long CondVarAddress = (long)ThreadState.X1;
int ThreadHandle = (int)ThreadState.X2;
- ulong Timeout = ThreadState.X3;
+ long Timeout = (long)ThreadState.X3;
Device.Log.PrintDebug(LogClass.KernelSvc,
"MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " +
@@ -128,86 +190,54 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
- KThread Thread = Process.HandleTable.GetData<KThread>(ThreadHandle);
-
- if (Thread == null)
- {
- Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!");
-
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle);
-
- return;
- }
-
- KThread WaitThread = Process.GetThread(ThreadState.Tpidr);
+ long Result = System.AddressArbiter.WaitProcessWideKeyAtomic(
+ Memory,
+ MutexAddress,
+ CondVarAddress,
+ ThreadHandle,
+ Timeout);
- if (!CondVarWait(WaitThread, ThreadHandle, MutexAddress, CondVarAddress, Timeout))
+ if (Result != 0)
{
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.Timeout);
-
- return;
+ if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout))
+ {
+ Device.Log.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ }
+ else
+ {
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
+ }
}
- ThreadState.X0 = 0;
+ ThreadState.X0 = (ulong)Result;
}
private void SvcSignalProcessWideKey(AThreadState ThreadState)
{
- long CondVarAddress = (long)ThreadState.X0;
- int Count = (int)ThreadState.X1;
+ long Address = (long)ThreadState.X0;
+ int Count = (int)ThreadState.X1;
Device.Log.PrintDebug(LogClass.KernelSvc,
- "CondVarAddress = 0x" + CondVarAddress.ToString("x16") + ", " +
- "Count = 0x" + Count .ToString("x8"));
+ "Address = 0x" + Address.ToString("x16") + ", " +
+ "Count = 0x" + Count .ToString("x8"));
- KThread CurrThread = Process.GetThread(ThreadState.Tpidr);
-
- CondVarSignal(ThreadState, CurrThread, CondVarAddress, Count);
+ System.AddressArbiter.SignalProcessWideKey(Process, Memory, Address, Count);
ThreadState.X0 = 0;
}
- private void MutexLock(
- KThread CurrThread,
- KThread WaitThread,
- int OwnerThreadHandle,
- int WaitThreadHandle,
- long MutexAddress)
- {
- lock (Process.ThreadSyncLock)
- {
- int MutexValue = Memory.ReadInt32(MutexAddress);
-
- Device.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = 0x" + MutexValue.ToString("x8"));
-
- if (MutexValue != (OwnerThreadHandle | MutexHasListenersMask))
- {
- return;
- }
-
- CurrThread.WaitHandle = WaitThreadHandle;
- CurrThread.MutexAddress = MutexAddress;
-
- InsertWaitingMutexThreadUnsafe(OwnerThreadHandle, WaitThread);
- }
-
- Device.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
-
- Process.Scheduler.EnterWait(CurrThread);
- }
-
private void SvcWaitForAddress(AThreadState ThreadState)
{
- long Address = (long)ThreadState.X0;
+ long Address = (long)ThreadState.X0;
ArbitrationType Type = (ArbitrationType)ThreadState.X1;
- int Value = (int)ThreadState.X2;
- ulong Timeout = ThreadState.X3;
+ int Value = (int)ThreadState.X2;
+ long Timeout = (long)ThreadState.X3;
Device.Log.PrintDebug(LogClass.KernelSvc,
- "Address = 0x" + Address.ToString("x16") + ", " +
- "ArbitrationType = 0x" + Type .ToString() + ", " +
- "Value = 0x" + Value .ToString("x8") + ", " +
- "Timeout = 0x" + Timeout.ToString("x16"));
+ "Address = 0x" + Address.ToString("x16") + ", " +
+ "Type = " + Type .ToString() + ", " +
+ "Value = 0x" + Value .ToString("x8") + ", " +
+ "Timeout = 0x" + Timeout.ToString("x16"));
if (IsPointingInsideKernel(Address))
{
@@ -227,287 +257,93 @@ namespace Ryujinx.HLE.HOS.Kernel
return;
}
+ long Result;
+
switch (Type)
{
case ArbitrationType.WaitIfLessThan:
- ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, false);
+ Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, false, Timeout);
break;
case ArbitrationType.DecrementAndWaitIfLessThan:
- ThreadState.X0 = AddressArbiter.WaitForAddressIfLessThan(Process, ThreadState, Memory, Address, Value, Timeout, true);
+ Result = System.AddressArbiter.WaitForAddressIfLessThan(Memory, Address, Value, true, Timeout);
break;
case ArbitrationType.WaitIfEqual:
- ThreadState.X0 = AddressArbiter.WaitForAddressIfEqual(Process, ThreadState, Memory, Address, Value, Timeout);
+ Result = System.AddressArbiter.WaitForAddressIfEqual(Memory, Address, Value, Timeout);
break;
default:
- ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
+ Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
break;
}
- }
- private void MutexUnlock(KThread CurrThread, long MutexAddress)
- {
- lock (Process.ThreadSyncLock)
+ if (Result != 0)
{
- //This is the new thread that will now own the mutex.
- //If no threads are waiting for the lock, then it should be null.
- (KThread OwnerThread, int Count) = PopMutexThreadUnsafe(CurrThread, MutexAddress);
-
- if (OwnerThread == CurrThread)
- {
- throw new InvalidOperationException();
- }
-
- if (OwnerThread != null)
- {
- //Remove all waiting mutex from the old owner,
- //and insert then on the new owner.
- UpdateMutexOwnerUnsafe(CurrThread, OwnerThread, MutexAddress);
-
- CurrThread.UpdatePriority();
-
- int HasListeners = Count >= 2 ? MutexHasListenersMask : 0;
-
- Memory.WriteInt32ToSharedAddr(MutexAddress, HasListeners | OwnerThread.WaitHandle);
-
- OwnerThread.WaitHandle = 0;
- OwnerThread.MutexAddress = 0;
- OwnerThread.CondVarAddress = 0;
- OwnerThread.MutexOwner = null;
-
- OwnerThread.UpdatePriority();
-
- Process.Scheduler.WakeUp(OwnerThread);
-
- Device.Log.PrintDebug(LogClass.KernelSvc, "Gave mutex to thread id " + OwnerThread.ThreadId + "!");
- }
- else
- {
- Memory.WriteInt32ToSharedAddr(MutexAddress, 0);
-
- Device.Log.PrintDebug(LogClass.KernelSvc, "No threads waiting mutex!");
- }
- }
- }
-
- private bool CondVarWait(
- KThread WaitThread,
- int WaitThreadHandle,
- long MutexAddress,
- long CondVarAddress,
- ulong Timeout)
- {
- WaitThread.WaitHandle = WaitThreadHandle;
- WaitThread.MutexAddress = MutexAddress;
- WaitThread.CondVarAddress = CondVarAddress;
-
- lock (Process.ThreadSyncLock)
- {
- MutexUnlock(WaitThread, MutexAddress);
-
- WaitThread.CondVarSignaled = false;
-
- Process.ThreadArbiterList.Add(WaitThread);
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
- Device.Log.PrintDebug(LogClass.KernelSvc, "Entering wait state...");
-
- if (Timeout != ulong.MaxValue)
- {
- Process.Scheduler.EnterWait(WaitThread, NsTimeConverter.GetTimeMs(Timeout));
-
- lock (Process.ThreadSyncLock)
- {
- if (!WaitThread.CondVarSignaled || WaitThread.MutexOwner != null)
- {
- if (WaitThread.MutexOwner != null)
- {
- WaitThread.MutexOwner.MutexWaiters.Remove(WaitThread);
- WaitThread.MutexOwner.UpdatePriority();
-
- WaitThread.MutexOwner = null;
- }
-
- Process.ThreadArbiterList.Remove(WaitThread);
-
- Device.Log.PrintDebug(LogClass.KernelSvc, "Timed out...");
-
- return false;
- }
- }
- }
- else
- {
- Process.Scheduler.EnterWait(WaitThread);
- }
-
- return true;
+ ThreadState.X0 = (ulong)Result;
}
- private void CondVarSignal(
- AThreadState ThreadState,
- KThread CurrThread,
- long CondVarAddress,
- int Count)
+ private void SvcSignalToAddress(AThreadState ThreadState)
{
- lock (Process.ThreadSyncLock)
- {
- while (Count == -1 || Count-- > 0)
- {
- KThread WaitThread = PopCondVarThreadUnsafe(CondVarAddress);
-
- if (WaitThread == null)
- {
- Device.Log.PrintDebug(LogClass.KernelSvc, "No more threads to wake up!");
-
- break;
- }
-
- WaitThread.CondVarSignaled = true;
-
- long MutexAddress = WaitThread.MutexAddress;
-
- Memory.SetExclusive(ThreadState, MutexAddress);
-
- int MutexValue = Memory.ReadInt32(MutexAddress);
-
- while (MutexValue != 0)
- {
- if (Memory.TestExclusive(ThreadState, MutexAddress))
- {
- //Wait until the lock is released.
- InsertWaitingMutexThreadUnsafe(MutexValue & ~MutexHasListenersMask, WaitThread);
+ long Address = (long)ThreadState.X0;
+ SignalType Type = (SignalType)ThreadState.X1;
+ int Value = (int)ThreadState.X2;
+ int Count = (int)ThreadState.X3;
- Memory.WriteInt32(MutexAddress, MutexValue | MutexHasListenersMask);
-
- Memory.ClearExclusiveForStore(ThreadState);
-
- break;
- }
-
- Memory.SetExclusive(ThreadState, MutexAddress);
-
- MutexValue = Memory.ReadInt32(MutexAddress);
- }
-
- Device.Log.PrintDebug(LogClass.KernelSvc, "MutexValue = 0x" + MutexValue.ToString("x8"));
-
- if (MutexValue == 0)
- {
- //Give the lock to this thread.
- Memory.WriteInt32ToSharedAddr(MutexAddress, WaitThread.WaitHandle);
-
- WaitThread.WaitHandle = 0;
- WaitThread.MutexAddress = 0;
- WaitThread.CondVarAddress = 0;
-
- WaitThread.MutexOwner?.UpdatePriority();
-
- WaitThread.MutexOwner = null;
-
- Process.Scheduler.WakeUp(WaitThread);
- }
- }
- }
- }
+ Device.Log.PrintDebug(LogClass.KernelSvc,
+ "Address = 0x" + Address.ToString("x16") + ", " +
+ "Type = " + Type .ToString() + ", " +
+ "Value = 0x" + Value .ToString("x8") + ", " +
+ "Count = 0x" + Count .ToString("x8"));
- private void UpdateMutexOwnerUnsafe(KThread CurrThread, KThread NewOwner, long MutexAddress)
- {
- //Go through all threads waiting for the mutex,
- //and update the MutexOwner field to point to the new owner.
- for (int Index = 0; Index < CurrThread.MutexWaiters.Count; Index++)
+ if (IsPointingInsideKernel(Address))
{
- KThread Thread = CurrThread.MutexWaiters[Index];
-
- if (Thread.MutexAddress == MutexAddress)
- {
- CurrThread.MutexWaiters.RemoveAt(Index--);
-
- InsertWaitingMutexThreadUnsafe(NewOwner, Thread);
- }
- }
- }
-
- private void InsertWaitingMutexThreadUnsafe(int OwnerThreadHandle, KThread WaitThread)
- {
- KThread OwnerThread = Process.HandleTable.GetData<KThread>(OwnerThreadHandle);
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!");
- if (OwnerThread == null)
- {
- Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{OwnerThreadHandle:x8}!");
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm);
return;
}
- InsertWaitingMutexThreadUnsafe(OwnerThread, WaitThread);
- }
-
- private void InsertWaitingMutexThreadUnsafe(KThread OwnerThread, KThread WaitThread)
- {
- WaitThread.MutexOwner = OwnerThread;
-
- if (!OwnerThread.MutexWaiters.Contains(WaitThread))
- {
- OwnerThread.MutexWaiters.Add(WaitThread);
-
- OwnerThread.UpdatePriority();
- }
- }
-
- private (KThread, int) PopMutexThreadUnsafe(KThread OwnerThread, long MutexAddress)
- {
- int Count = 0;
-
- KThread WakeThread = null;
-
- foreach (KThread Thread in OwnerThread.MutexWaiters)
+ if (IsAddressNotWordAligned(Address))
{
- if (Thread.MutexAddress != MutexAddress)
- {
- continue;
- }
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!");
- if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority)
- {
- WakeThread = Thread;
- }
+ ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress);
- Count++;
+ return;
}
- if (WakeThread != null)
- {
- OwnerThread.MutexWaiters.Remove(WakeThread);
- }
+ long Result;
- return (WakeThread, Count);
- }
+ switch (Type)
+ {
+ case SignalType.Signal:
+ Result = System.AddressArbiter.Signal(Address, Count);
+ break;
- private KThread PopCondVarThreadUnsafe(long CondVarAddress)
- {
- KThread WakeThread = null;
+ case SignalType.SignalAndIncrementIfEqual:
+ Result = System.AddressArbiter.SignalAndIncrementIfEqual(Memory, Address, Value, Count);
+ break;
- foreach (KThread Thread in Process.ThreadArbiterList)
- {
- if (Thread.CondVarAddress != CondVarAddress)
- {
- continue;
- }
+ case SignalType.SignalAndModifyIfEqual:
+ Result = System.AddressArbiter.SignalAndModifyIfEqual(Memory, Address, Value, Count);
+ break;
- if (WakeThread == null || Thread.ActualPriority < WakeThread.ActualPriority)
- {
- WakeThread = Thread;
- }
+ default:
+ Result = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue);
+ break;
}
- if (WakeThread != null)
+ if (Result != 0)
{
- Process.ThreadArbiterList.Remove(WakeThread);
+ Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!");
}
- return WakeThread;
+ ThreadState.X0 = (ulong)Result;
}
private bool IsPointingInsideKernel(long Address)