Implement EventType

This commit is contained in:
gdk 2023-01-01 00:44:50 -03:00
parent ce86b81961
commit 6ed5c58abe
20 changed files with 334 additions and 56 deletions

View file

@ -1228,17 +1228,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
foreach (KThread thread in WaitingThreads) foreach (KThread thread in WaitingThreads)
{ {
WakeAndSetResult(thread, result); WakeAndSetResult(thread, result, this);
} }
KernelContext.CriticalSection.Leave(); 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) if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{ {
thread.SignaledObj = null; thread.SignaledObj = signaledObj;
thread.ObjSyncResult = result; thread.ObjSyncResult = result;
thread.Reschedule(ThreadSchedState.Running); thread.Reschedule(ThreadSchedState.Running);

View file

@ -22,8 +22,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public bool Running { get; private set; } = true; public bool Running { get; private set; } = true;
public ulong GetX(int index) => 0UL; private readonly ulong[] _x = new ulong[32];
public void SetX(int index, ulong value) { }
public ulong GetX(int index) => _x[index];
public void SetX(int index, ulong value) => _x[index] = value;
public V128 GetV(int index) => default; public V128 GetV(int index) => default;
public void SetV(int index, V128 value) { } public void SetV(int index, V128 value) { }

View file

@ -11,6 +11,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
public bool Running => _context.Running; public bool Running => _context.Running;
public ulong TlsAddress => (ulong)_context.TpidrroEl0; public ulong TlsAddress => (ulong)_context.TpidrroEl0;
public ulong GetX(int index) => _context.GetX(index);
private int _locked; private int _locked;
public KThreadContext(IExecutionContext context) public KThreadContext(IExecutionContext context)

View file

@ -11,6 +11,7 @@ namespace Ryujinx.Horizon.Common
Result CloseHandle(int handle); Result CloseHandle(int handle);
Result WaitSynchronization(out int handleIndex, ReadOnlySpan<int> handles, long timeout); Result WaitSynchronization(out int handleIndex, ReadOnlySpan<int> handles, long timeout);
Result CancelSynchronization(int handle);
Result GetProcessId(out ulong pid, int handle); Result GetProcessId(out ulong pid, int handle);

View file

@ -1,11 +1,11 @@
using System.Runtime.Intrinsics; namespace Ryujinx.Horizon.Common
namespace Ryujinx.Horizon.Common
{ {
public interface IThreadContext public interface IThreadContext
{ {
bool Running { get; } bool Running { get; }
ulong TlsAddress { get; } ulong TlsAddress { get; }
ulong GetX(int index);
} }
} }

View file

@ -2,7 +2,7 @@
namespace Ryujinx.Horizon.Common namespace Ryujinx.Horizon.Common
{ {
class InvalidResultException : Exception public class InvalidResultException : Exception
{ {
public InvalidResultException() public InvalidResultException()
{ {

View file

@ -76,6 +76,11 @@ namespace Ryujinx.Horizon.Common
public void AbortOnFailure() public void AbortOnFailure()
{ {
if (this == KernelResult.ThreadTerminating)
{
throw new ThreadTerminatedException();
}
AbortUnless(Success); AbortUnless(Success);
} }

View file

@ -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)
{
}
}
}

View file

@ -18,21 +18,27 @@ namespace Ryujinx.Horizon
[ThreadStatic] [ThreadStatic]
private static IThreadContext _threadContext; private static IThreadContext _threadContext;
[ThreadStatic]
private static int _threadHandle;
public static HorizonOptions Options => _options; public static HorizonOptions Options => _options;
public static ISyscallApi Syscall => _syscall; public static ISyscallApi Syscall => _syscall;
public static IVirtualMemoryManager AddressSpace => _addressSpace; public static IVirtualMemoryManager AddressSpace => _addressSpace;
public static IThreadContext ThreadContext => _threadContext; public static IThreadContext ThreadContext => _threadContext;
public static int CurrentThreadHandle => _threadHandle;
public static void Register( public static void Register(
HorizonOptions options, HorizonOptions options,
ISyscallApi syscallApi, ISyscallApi syscallApi,
IVirtualMemoryManager addressSpace, IVirtualMemoryManager addressSpace,
IThreadContext threadContext) IThreadContext threadContext,
int threadHandle)
{ {
_options = options; _options = options;
_syscall = syscallApi; _syscall = syscallApi;
_addressSpace = addressSpace; _addressSpace = addressSpace;
_threadContext = threadContext; _threadContext = threadContext;
_threadHandle = threadHandle;
} }
} }
} }

View file

@ -1,26 +1,61 @@
namespace Ryujinx.Horizon.Sdk.OsTypes using System;
{ using System.Collections.Generic;
class Event
{
// TODO: Actually implement this.
private bool _autoClear; namespace Ryujinx.Horizon.Sdk.OsTypes
private bool _isSignaled;
public Event(bool autoClear, bool isSignaled = false)
{ {
_autoClear = autoClear; class Event : IDisposable
_isSignaled = isSignaled; {
private EventType _event;
public object EventLock => _event.Lock;
public LinkedList<MultiWaitHolderBase> MultiWaitHolders => _event.MultiWaitHolders;
public Event(EventClearMode clearMode)
{
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() 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);
} }
} }
} }

View file

@ -1,11 +1,15 @@
namespace Ryujinx.Horizon.Sdk.OsTypes using System.Collections.Generic;
namespace Ryujinx.Horizon.Sdk.OsTypes
{ {
struct EventType struct EventType
{ {
public LinkedList<MultiWaitHolderBase> MultiWaitHolders;
public bool Signaled; public bool Signaled;
public bool InitiallySignaled; public bool InitiallySignaled;
public byte ClearMode; public EventClearMode ClearMode;
public InitializationState State; public InitializationState State;
public ulong BroadcastCounter; public ulong BroadcastCounter;
public object Lock;
} }
} }

View file

@ -52,9 +52,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
public MultiWaitHolderBase WaitAnyImpl(bool infinite, long timeout) public MultiWaitHolderBase WaitAnyImpl(bool infinite, long timeout)
{ {
_waitingThreadHandle = Os.GetCurrentThreadHandle();
_signaledHolder = null; _signaledHolder = null;
_waitingThreadHandle = Os.GetCurrentThreadHandle();
MultiWaitHolderBase result = LinkHoldersToObjectList(); MultiWaitHolderBase result = LinkHoldersToObjectList();
@ -72,6 +71,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
} }
UnlinkHoldersFromObjectsList(); UnlinkHoldersFromObjectsList();
_waitingThreadHandle = 0;
return result; return result;
} }
@ -84,13 +84,13 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
int count = FillObjectsArray(objectHandles, objects); int count = FillObjectsArray(objectHandles, objects);
long endTime = infinite ? -1L : PerformanceCounter.ElapsedMilliseconds * 1000000; long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000;
while (true) while (true)
{ {
CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000; CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000;
MultiWaitHolderBase minTimeoutObject = RecalculateNextTimepoint(endTime, out long minTimeout); MultiWaitHolderBase minTimeoutObject = RecalcMultiWaitTimeout(endTime, out long minTimeout);
int index; int index;
@ -164,7 +164,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
return count; return count;
} }
private MultiWaitHolderBase RecalculateNextTimepoint(long endTime, out long minTimeout) private MultiWaitHolderBase RecalcMultiWaitTimeout(long endTime, out long minTimeout)
{ {
MultiWaitHolderBase minTimeHolder = null; MultiWaitHolderBase minTimeHolder = null;
@ -172,7 +172,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
foreach (MultiWaitHolder holder in _multiWaits) foreach (MultiWaitHolder holder in _multiWaits)
{ {
long currentTime = holder.GetWakeUpTime(); long currentTime = holder.GetAbsoluteTimeToWakeup();
if ((ulong)currentTime < (ulong)minTime) if ((ulong)currentTime < (ulong)minTime)
{ {
@ -207,6 +207,18 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
return index; return index;
} }
public void NotifyAndWakeUpThread(MultiWaitHolderBase holder)
{
lock (_lock)
{
if (_signaledHolder == null)
{
_signaledHolder = holder;
HorizonStatic.Syscall.CancelSynchronization(_waitingThreadHandle).AbortOnFailure();
}
}
}
private MultiWaitHolderBase LinkHoldersToObjectList() private MultiWaitHolderBase LinkHoldersToObjectList()
{ {
MultiWaitHolderBase signaledHolder = null; MultiWaitHolderBase signaledHolder = null;

View file

@ -17,10 +17,23 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
MultiWait = multiWait; 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;
}
} }
} }

View file

@ -0,0 +1,44 @@
using System.Collections.Generic;
namespace Ryujinx.Horizon.Sdk.OsTypes
{
class MultiWaitHolderOfEvent : MultiWaitHolder
{
private Event _event;
private LinkedListNode<MultiWaitHolderBase> _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;
}
}
}
}

View file

@ -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 static partial class Os
{ {
@ -6,11 +10,121 @@
{ {
evnt = new EventType evnt = new EventType
{ {
MultiWaitHolders = new LinkedList<MultiWaitHolderBase>(),
Signaled = signaled, Signaled = signaled,
InitiallySignaled = signaled, InitiallySignaled = signaled,
ClearMode = (byte)clearMode, ClearMode = clearMode,
State = InitializationState.Initialized 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;
}
}
} }
} }

View file

@ -19,7 +19,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
return result; return result;
} }
sysEvent.State = SystemEventType.InitializatonState.InitializedAsInterProcess; sysEvent.State = SystemEventType.InitializationState.InitializedAsInterProcess;
} }
else else
{ {
@ -32,11 +32,11 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
public static void DestroySystemEvent(ref SystemEventType sysEvent) public static void DestroySystemEvent(ref SystemEventType sysEvent)
{ {
var oldState = sysEvent.State; var oldState = sysEvent.State;
sysEvent.State = SystemEventType.InitializatonState.NotInitialized; sysEvent.State = SystemEventType.InitializationState.NotInitialized;
switch (oldState) switch (oldState)
{ {
case SystemEventType.InitializatonState.InitializedAsInterProcess: case SystemEventType.InitializationState.InitializedAsInterProcess:
InterProcessEvent.Destroy(ref sysEvent.InterProcessEvent); InterProcessEvent.Destroy(ref sysEvent.InterProcessEvent);
break; break;
} }
@ -66,7 +66,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
{ {
switch (sysEvent.State) switch (sysEvent.State)
{ {
case SystemEventType.InitializatonState.InitializedAsInterProcess: case SystemEventType.InitializationState.InitializedAsInterProcess:
InterProcessEvent.Signal(ref sysEvent.InterProcessEvent); InterProcessEvent.Signal(ref sysEvent.InterProcessEvent);
break; break;
} }
@ -76,7 +76,7 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
{ {
switch (sysEvent.State) switch (sysEvent.State)
{ {
case SystemEventType.InitializatonState.InitializedAsInterProcess: case SystemEventType.InitializationState.InitializedAsInterProcess:
InterProcessEvent.Clear(ref sysEvent.InterProcessEvent); InterProcessEvent.Clear(ref sysEvent.InterProcessEvent);
break; break;
} }

View file

@ -4,9 +4,7 @@
{ {
public static int GetCurrentThreadHandle() public static int GetCurrentThreadHandle()
{ {
// TODO. return HorizonStatic.CurrentThreadHandle;
return 0;
} }
} }
} }

View file

@ -2,7 +2,7 @@
{ {
struct SystemEventType struct SystemEventType
{ {
public enum InitializatonState : byte public enum InitializationState : byte
{ {
NotInitialized, NotInitialized,
InitializedAsEvent, InitializedAsEvent,
@ -10,8 +10,8 @@
} }
public InterProcessEventType InterProcessEvent; public InterProcessEventType InterProcessEvent;
public InitializatonState State; public InitializationState State;
public bool NotInitialized => State == InitializatonState.NotInitialized; public bool NotInitialized => State == InitializationState.NotInitialized;
} }
} }

View file

@ -3,7 +3,6 @@ using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.Sdk.Sf.Cmif; using Ryujinx.Horizon.Sdk.Sf.Cmif;
using Ryujinx.Horizon.Sdk.Sm; using Ryujinx.Horizon.Sdk.Sm;
using System; using System;
using Ryujinx.Horizon.Sm;
namespace Ryujinx.Horizon.Sdk.Sf.Hipc namespace Ryujinx.Horizon.Sdk.Sf.Hipc
{ {
@ -19,6 +18,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
private readonly object _waitableSelectionLock; private readonly object _waitableSelectionLock;
private readonly object _waitListLock; private readonly object _waitListLock;
private readonly Event _requestStopEvent;
private readonly Event _notifyEvent; private readonly Event _notifyEvent;
private readonly MultiWaitHolderBase _requestStopEventHolder; private readonly MultiWaitHolderBase _requestStopEventHolder;
@ -41,7 +41,13 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
_waitableSelectionLock = new object(); _waitableSelectionLock = new object();
_waitListLock = 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) public void RegisterObjectForServer(IServiceObject staticObject, int portHandle)
@ -109,17 +115,24 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
private bool WaitAndProcessRequestsImpl() private bool WaitAndProcessRequestsImpl()
{ {
MultiWaitHolder waitable = WaitSignaled(); try
{
MultiWaitHolder multiWait = WaitSignaled();
if (waitable == null) if (multiWait == null)
{ {
return false; return false;
} }
DebugUtil.Assert(Process(waitable).IsSuccess); DebugUtil.Assert(Process(multiWait).IsSuccess);
return HorizonStatic.ThreadContext.Running; return HorizonStatic.ThreadContext.Running;
} }
catch (ThreadTerminatedException)
{
return false;
}
}
private MultiWaitHolder WaitSignaled() private MultiWaitHolder WaitSignaled()
{ {
@ -137,7 +150,7 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
} }
else if (selected == _notifyEventHolder) else if (selected == _notifyEventHolder)
{ {
_notifyEvent.Reset(); _notifyEvent.Clear();
} }
else 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) protected override void RegisterSessionToWaitList(ServerSession session)
{ {
session.HasReceived = false; session.HasReceived = false;

View file

@ -17,7 +17,7 @@ namespace Ryujinx.Horizon
public void Start(ISyscallApi syscallApi, IVirtualMemoryManager addressSpace, IThreadContext threadContext) 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(); _entrypoint();
} }