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)
{
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);

View file

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

View file

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

View file

@ -11,6 +11,7 @@ namespace Ryujinx.Horizon.Common
Result CloseHandle(int handle);
Result WaitSynchronization(out int handleIndex, ReadOnlySpan<int> handles, long timeout);
Result CancelSynchronization(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
{
bool Running { get; }
ulong TlsAddress { get; }
ulong GetX(int index);
}
}

View file

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

View file

@ -76,6 +76,11 @@ namespace Ryujinx.Horizon.Common
public void AbortOnFailure()
{
if (this == KernelResult.ThreadTerminating)
{
throw new ThreadTerminatedException();
}
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]
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;
}
}
}

View file

@ -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<MultiWaitHolderBase> 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);
}
}
}

View file

@ -1,11 +1,15 @@
namespace Ryujinx.Horizon.Sdk.OsTypes
using System.Collections.Generic;
namespace Ryujinx.Horizon.Sdk.OsTypes
{
struct EventType
{
public LinkedList<MultiWaitHolderBase> MultiWaitHolders;
public bool Signaled;
public bool InitiallySignaled;
public byte ClearMode;
public EventClearMode ClearMode;
public InitializationState State;
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)
{
_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;

View file

@ -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;
}
}
}

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
{
@ -6,11 +10,121 @@
{
evnt = new EventType
{
MultiWaitHolders = new LinkedList<MultiWaitHolderBase>(),
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;
}
}
}
}

View file

@ -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;
}

View file

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

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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();
}