diff --git a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs index 1902a5d64..9c2184d92 100644 --- a/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs +++ b/Ryujinx.HLE/HOS/Kernel/Ipc/KServerSession.cs @@ -1228,17 +1228,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc foreach (KThread thread in WaitingThreads) { - WakeAndSetResult(thread, result); + WakeAndSetResult(thread, result, this); } KernelContext.CriticalSection.Leave(); } - private void WakeAndSetResult(KThread thread, Result result) + private void WakeAndSetResult(KThread thread, Result result, KSynchronizationObject signaledObj = null) { if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) { - thread.SignaledObj = null; + thread.SignaledObj = signaledObj; thread.ObjSyncResult = result; thread.Reschedule(ThreadSchedState.Running); diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs index f37ff2af3..77fcdf33b 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/Process/ProcessExecutionContext.cs @@ -22,8 +22,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process public bool Running { get; private set; } = true; - public ulong GetX(int index) => 0UL; - public void SetX(int index, ulong value) { } + private readonly ulong[] _x = new ulong[32]; + + public ulong GetX(int index) => _x[index]; + public void SetX(int index, ulong value) => _x[index] = value; public V128 GetV(int index) => default; public void SetV(int index, V128 value) { } diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs index 16a8b0bf0..e8ad53c28 100644 --- a/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs +++ b/Ryujinx.HLE/HOS/Kernel/Threading/KThreadContext.cs @@ -11,6 +11,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading public bool Running => _context.Running; public ulong TlsAddress => (ulong)_context.TpidrroEl0; + public ulong GetX(int index) => _context.GetX(index); + private int _locked; public KThreadContext(IExecutionContext context) diff --git a/Ryujinx.Horizon.Common/ISyscallApi.cs b/Ryujinx.Horizon.Common/ISyscallApi.cs index 6ea793b3b..8fa276b52 100644 --- a/Ryujinx.Horizon.Common/ISyscallApi.cs +++ b/Ryujinx.Horizon.Common/ISyscallApi.cs @@ -11,6 +11,7 @@ namespace Ryujinx.Horizon.Common Result CloseHandle(int handle); Result WaitSynchronization(out int handleIndex, ReadOnlySpan handles, long timeout); + Result CancelSynchronization(int handle); Result GetProcessId(out ulong pid, int handle); diff --git a/Ryujinx.Horizon.Common/IThreadContext.cs b/Ryujinx.Horizon.Common/IThreadContext.cs index a63dfb9e4..47aea1a38 100644 --- a/Ryujinx.Horizon.Common/IThreadContext.cs +++ b/Ryujinx.Horizon.Common/IThreadContext.cs @@ -1,11 +1,11 @@ -using System.Runtime.Intrinsics; - -namespace Ryujinx.Horizon.Common +namespace Ryujinx.Horizon.Common { public interface IThreadContext { bool Running { get; } ulong TlsAddress { get; } + + ulong GetX(int index); } } diff --git a/Ryujinx.Horizon.Common/InvalidResultException.cs b/Ryujinx.Horizon.Common/InvalidResultException.cs index 571c35833..cf38b6403 100644 --- a/Ryujinx.Horizon.Common/InvalidResultException.cs +++ b/Ryujinx.Horizon.Common/InvalidResultException.cs @@ -2,7 +2,7 @@ namespace Ryujinx.Horizon.Common { - class InvalidResultException : Exception + public class InvalidResultException : Exception { public InvalidResultException() { diff --git a/Ryujinx.Horizon.Common/Result.cs b/Ryujinx.Horizon.Common/Result.cs index 5d192b4cd..04281199d 100644 --- a/Ryujinx.Horizon.Common/Result.cs +++ b/Ryujinx.Horizon.Common/Result.cs @@ -76,6 +76,11 @@ namespace Ryujinx.Horizon.Common public void AbortOnFailure() { + if (this == KernelResult.ThreadTerminating) + { + throw new ThreadTerminatedException(); + } + AbortUnless(Success); } diff --git a/Ryujinx.Horizon.Common/ThreadTerminatedException.cs b/Ryujinx.Horizon.Common/ThreadTerminatedException.cs new file mode 100644 index 000000000..c86cb05fa --- /dev/null +++ b/Ryujinx.Horizon.Common/ThreadTerminatedException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Ryujinx.Horizon.Common +{ + public class ThreadTerminatedException : Exception + { + public ThreadTerminatedException() : base("The thread has been terminated.") + { + } + + public ThreadTerminatedException(string message) : base(message) + { + } + + public ThreadTerminatedException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/Ryujinx.Horizon/HorizonStatic.cs b/Ryujinx.Horizon/HorizonStatic.cs index 989bba045..1e483cd44 100644 --- a/Ryujinx.Horizon/HorizonStatic.cs +++ b/Ryujinx.Horizon/HorizonStatic.cs @@ -18,21 +18,27 @@ namespace Ryujinx.Horizon [ThreadStatic] private static IThreadContext _threadContext; + [ThreadStatic] + private static int _threadHandle; + public static HorizonOptions Options => _options; public static ISyscallApi Syscall => _syscall; public static IVirtualMemoryManager AddressSpace => _addressSpace; public static IThreadContext ThreadContext => _threadContext; + public static int CurrentThreadHandle => _threadHandle; public static void Register( HorizonOptions options, ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, - IThreadContext threadContext) + IThreadContext threadContext, + int threadHandle) { _options = options; _syscall = syscallApi; _addressSpace = addressSpace; _threadContext = threadContext; + _threadHandle = threadHandle; } } } diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Event.cs b/Ryujinx.Horizon/Sdk/OsTypes/Event.cs index 0e753bd18..79d7408e3 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/Event.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/Event.cs @@ -1,26 +1,61 @@ -namespace Ryujinx.Horizon.Sdk.OsTypes +using System; +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.OsTypes { - class Event + class Event : IDisposable { - // TODO: Actually implement this. + private EventType _event; - private bool _autoClear; - private bool _isSignaled; + public object EventLock => _event.Lock; + public LinkedList MultiWaitHolders => _event.MultiWaitHolders; - public Event(bool autoClear, bool isSignaled = false) + public Event(EventClearMode clearMode) { - _autoClear = autoClear; - _isSignaled = isSignaled; + Os.InitializeEvent(out _event, signaled: false, clearMode); } - public void Reset() + public TriBool IsSignaledThreadUnsafe() { - _isSignaled = false; + return _event.Signaled ? TriBool.True : TriBool.False; + } + + public void Wait() + { + Os.WaitEvent(ref _event); + } + + public bool TryWait() + { + return Os.TryWaitEvent(ref _event); + } + + public bool TimedWait(TimeSpan timeout) + { + return Os.TimedWaitEvent(ref _event, timeout); } public void Signal() { - _isSignaled = true; + Os.SignalEvent(ref _event); + } + + public void Clear() + { + Os.ClearEvent(ref _event); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Os.FinalizeEvent(ref _event); + } + } + + public void Dispose() + { + Dispose(true); } } } diff --git a/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs b/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs index bff9c70c1..b4b1a275c 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs @@ -1,11 +1,15 @@ -namespace Ryujinx.Horizon.Sdk.OsTypes +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.OsTypes { struct EventType { + public LinkedList MultiWaitHolders; public bool Signaled; public bool InitiallySignaled; - public byte ClearMode; + public EventClearMode ClearMode; public InitializationState State; public ulong BroadcastCounter; + public object Lock; } } diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs index 5903d2004..71481e9df 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs @@ -52,9 +52,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl public MultiWaitHolderBase WaitAnyImpl(bool infinite, long timeout) { - _waitingThreadHandle = Os.GetCurrentThreadHandle(); - _signaledHolder = null; + _waitingThreadHandle = Os.GetCurrentThreadHandle(); MultiWaitHolderBase result = LinkHoldersToObjectList(); @@ -72,6 +71,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl } UnlinkHoldersFromObjectsList(); + _waitingThreadHandle = 0; return result; } @@ -84,13 +84,13 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl int count = FillObjectsArray(objectHandles, objects); - long endTime = infinite ? -1L : PerformanceCounter.ElapsedMilliseconds * 1000000; + long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000; while (true) { CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000; - MultiWaitHolderBase minTimeoutObject = RecalculateNextTimepoint(endTime, out long minTimeout); + MultiWaitHolderBase minTimeoutObject = RecalcMultiWaitTimeout(endTime, out long minTimeout); int index; @@ -164,7 +164,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl return count; } - private MultiWaitHolderBase RecalculateNextTimepoint(long endTime, out long minTimeout) + private MultiWaitHolderBase RecalcMultiWaitTimeout(long endTime, out long minTimeout) { MultiWaitHolderBase minTimeHolder = null; @@ -172,7 +172,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl foreach (MultiWaitHolder holder in _multiWaits) { - long currentTime = holder.GetWakeUpTime(); + long currentTime = holder.GetAbsoluteTimeToWakeup(); if ((ulong)currentTime < (ulong)minTime) { @@ -207,6 +207,18 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl return index; } + public void NotifyAndWakeUpThread(MultiWaitHolderBase holder) + { + lock (_lock) + { + if (_signaledHolder == null) + { + _signaledHolder = holder; + HorizonStatic.Syscall.CancelSynchronization(_waitingThreadHandle).AbortOnFailure(); + } + } + } + private MultiWaitHolderBase LinkHoldersToObjectList() { MultiWaitHolderBase signaledHolder = null; diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs index 35286aef7..018305ba6 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs @@ -17,10 +17,23 @@ namespace Ryujinx.Horizon.Sdk.OsTypes MultiWait = multiWait; } - public virtual TriBool LinkToObjectList() => TriBool.Undefined; + public MultiWaitImpl GetMultiWait() + { + return MultiWait; + } - public virtual void UnlinkFromObjectList() { } + public virtual TriBool LinkToObjectList() + { + return TriBool.Undefined; + } - public virtual long GetWakeUpTime() => -1L; + public virtual void UnlinkFromObjectList() + { + } + + public virtual long GetAbsoluteTimeToWakeup() + { + return long.MaxValue; + } } } diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs new file mode 100644 index 000000000..381469c72 --- /dev/null +++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; + +namespace Ryujinx.Horizon.Sdk.OsTypes +{ + class MultiWaitHolderOfEvent : MultiWaitHolder + { + private Event _event; + private LinkedListNode _node; + + public override TriBool Signaled + { + get + { + lock (_event.EventLock) + { + return _event.IsSignaledThreadUnsafe(); + } + } + } + + public MultiWaitHolderOfEvent(Event evnt) + { + _event = evnt; + } + + public override TriBool LinkToObjectList() + { + lock (_event.EventLock) + { + _node = _event.MultiWaitHolders.AddLast(this); + return _event.IsSignaledThreadUnsafe(); + } + } + + public override void UnlinkFromObjectList() + { + lock (_event.EventLock) + { + _event.MultiWaitHolders.Remove(_node); + _node = null; + } + } + } +} diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs index 687e02f7a..cc7e84836 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs @@ -1,4 +1,8 @@ -namespace Ryujinx.Horizon.Sdk.OsTypes +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Horizon.Sdk.OsTypes { static partial class Os { @@ -6,11 +10,121 @@ { evnt = new EventType { + MultiWaitHolders = new LinkedList(), Signaled = signaled, InitiallySignaled = signaled, - ClearMode = (byte)clearMode, - State = InitializationState.Initialized + ClearMode = clearMode, + State = InitializationState.Initialized, + Lock = new object() }; } + + public static void FinalizeEvent(ref EventType evnt) + { + evnt.State = InitializationState.NotInitialized; + } + + public static void WaitEvent(ref EventType evnt) + { + lock (evnt.Lock) + { + ulong currentCounter = evnt.BroadcastCounter; + + while (!evnt.Signaled) + { + if (currentCounter != evnt.BroadcastCounter) + { + break; + } + + Monitor.Wait(evnt.Lock); + } + + if (evnt.ClearMode == EventClearMode.AutoClear) + { + evnt.Signaled = false; + } + } + } + + public static bool TryWaitEvent(ref EventType evnt) + { + lock (evnt.Lock) + { + bool signaled = evnt.Signaled; + + if (evnt.ClearMode == EventClearMode.AutoClear) + { + evnt.Signaled = false; + } + + return signaled; + } + } + + public static bool TimedWaitEvent(ref EventType evnt, TimeSpan timeout) + { + lock (evnt.Lock) + { + ulong currentCounter = evnt.BroadcastCounter; + + while (!evnt.Signaled) + { + if (currentCounter != evnt.BroadcastCounter) + { + break; + } + + bool wasSignaledInTime = Monitor.Wait(evnt.Lock, timeout); + if (!wasSignaledInTime) + { + return false; + } + } + + if (evnt.ClearMode == EventClearMode.AutoClear) + { + evnt.Signaled = false; + } + } + + return true; + } + + public static void SignalEvent(ref EventType evnt) + { + lock (evnt.Lock) + { + if (evnt.Signaled) + { + return; + } + + evnt.Signaled = true; + + if (evnt.ClearMode == EventClearMode.ManualClear) + { + evnt.BroadcastCounter++; + Monitor.PulseAll(evnt.Lock); + } + else + { + Monitor.Pulse(evnt.Lock); + } + + foreach (MultiWaitHolderBase holder in evnt.MultiWaitHolders) + { + holder.GetMultiWait().NotifyAndWakeUpThread(holder); + } + } + } + + public static void ClearEvent(ref EventType evnt) + { + lock (evnt.Lock) + { + evnt.Signaled = false; + } + } } } diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs index 631e752ea..061d7a3cd 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs @@ -19,7 +19,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes return result; } - sysEvent.State = SystemEventType.InitializatonState.InitializedAsInterProcess; + sysEvent.State = SystemEventType.InitializationState.InitializedAsInterProcess; } else { @@ -32,11 +32,11 @@ namespace Ryujinx.Horizon.Sdk.OsTypes public static void DestroySystemEvent(ref SystemEventType sysEvent) { var oldState = sysEvent.State; - sysEvent.State = SystemEventType.InitializatonState.NotInitialized; + sysEvent.State = SystemEventType.InitializationState.NotInitialized; switch (oldState) { - case SystemEventType.InitializatonState.InitializedAsInterProcess: + case SystemEventType.InitializationState.InitializedAsInterProcess: InterProcessEvent.Destroy(ref sysEvent.InterProcessEvent); break; } @@ -66,7 +66,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes { switch (sysEvent.State) { - case SystemEventType.InitializatonState.InitializedAsInterProcess: + case SystemEventType.InitializationState.InitializedAsInterProcess: InterProcessEvent.Signal(ref sysEvent.InterProcessEvent); break; } @@ -76,7 +76,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes { switch (sysEvent.State) { - case SystemEventType.InitializatonState.InitializedAsInterProcess: + case SystemEventType.InitializationState.InitializedAsInterProcess: InterProcessEvent.Clear(ref sysEvent.InterProcessEvent); break; } diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs index 131add14a..2037cd7fe 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs @@ -4,9 +4,7 @@ { public static int GetCurrentThreadHandle() { - // TODO. - - return 0; + return HorizonStatic.CurrentThreadHandle; } } } diff --git a/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs b/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs index 067cb7d68..338493d23 100644 --- a/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs +++ b/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs @@ -2,7 +2,7 @@ { struct SystemEventType { - public enum InitializatonState : byte + public enum InitializationState : byte { NotInitialized, InitializedAsEvent, @@ -10,8 +10,8 @@ } public InterProcessEventType InterProcessEvent; - public InitializatonState State; + public InitializationState State; - public bool NotInitialized => State == InitializatonState.NotInitialized; + public bool NotInitialized => State == InitializationState.NotInitialized; } } diff --git a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs index 46bfe2204..bf70aa501 100644 --- a/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs +++ b/Ryujinx.Horizon/Sdk/Sf/Hipc/ServerManagerBase.cs @@ -3,7 +3,6 @@ using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Sdk.Sf.Cmif; using Ryujinx.Horizon.Sdk.Sm; using System; -using Ryujinx.Horizon.Sm; namespace Ryujinx.Horizon.Sdk.Sf.Hipc { @@ -19,6 +18,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private readonly object _waitableSelectionLock; private readonly object _waitListLock; + private readonly Event _requestStopEvent; private readonly Event _notifyEvent; private readonly MultiWaitHolderBase _requestStopEventHolder; @@ -41,7 +41,13 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc _waitableSelectionLock = new object(); _waitListLock = new object(); - _notifyEvent = new Event(false); + _requestStopEvent = new Event(EventClearMode.ManualClear); + _notifyEvent = new Event(EventClearMode.ManualClear); + + _requestStopEventHolder = new MultiWaitHolderOfEvent(_requestStopEvent); + _multiWait.LinkMultiWaitHolder(_requestStopEventHolder); + _notifyEventHolder = new MultiWaitHolderOfEvent(_notifyEvent); + _multiWait.LinkMultiWaitHolder(_notifyEventHolder); } public void RegisterObjectForServer(IServiceObject staticObject, int portHandle) @@ -109,16 +115,23 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc private bool WaitAndProcessRequestsImpl() { - MultiWaitHolder waitable = WaitSignaled(); + try + { + MultiWaitHolder multiWait = WaitSignaled(); - if (waitable == null) + if (multiWait == null) + { + return false; + } + + DebugUtil.Assert(Process(multiWait).IsSuccess); + + return HorizonStatic.ThreadContext.Running; + } + catch (ThreadTerminatedException) { return false; } - - DebugUtil.Assert(Process(waitable).IsSuccess); - - return HorizonStatic.ThreadContext.Running; } private MultiWaitHolder WaitSignaled() @@ -137,7 +150,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } else if (selected == _notifyEventHolder) { - _notifyEvent.Reset(); + _notifyEvent.Clear(); } else { @@ -149,6 +162,16 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc } } + public void ResumeProcessing() + { + _requestStopEvent.Clear(); + } + + public void RequestStopProcessing() + { + _requestStopEvent.Signal(); + } + protected override void RegisterSessionToWaitList(ServerSession session) { session.HasReceived = false; diff --git a/Ryujinx.Horizon/ServiceEntry.cs b/Ryujinx.Horizon/ServiceEntry.cs index fabfc665e..3fea46c19 100644 --- a/Ryujinx.Horizon/ServiceEntry.cs +++ b/Ryujinx.Horizon/ServiceEntry.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Horizon public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext) { - HorizonStatic.Register(_options, syscallApi, addressSpace, threadContext); + HorizonStatic.Register(_options, syscallApi, addressSpace, threadContext, (int)threadContext.GetX(1)); _entrypoint(); }