aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Horizon/Sdk/OsTypes
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2023-01-04 19:15:45 -0300
committerGitHub <noreply@github.com>2023-01-04 23:15:45 +0100
commit08831eecf77cedd3c4192ebab5a9c485fb15d51e (patch)
tree6d95b921a18e9cfa477579fcecb9d041e03d682e /Ryujinx.Horizon/Sdk/OsTypes
parentc6a139a6e7e3ffe1591bc14dafafed60b9bef0dc (diff)
IPC refactor part 3+4: New server HIPC message processor (#4188)
* IPC refactor part 3 + 4: New server HIPC message processor with source generator based serialization * Make types match on calls to AlignUp/AlignDown * Formatting * Address some PR feedback * Move BitfieldExtensions to Ryujinx.Common.Utilities and consolidate implementations * Rename Reader/Writer to SpanReader/SpanWriter and move to Ryujinx.Common.Memory * Implement EventType * Address more PR feedback * Log request processing errors since they are not normal * Rename waitable to multiwait and add missing lock * PR feedback * Ac_K PR feedback
Diffstat (limited to 'Ryujinx.Horizon/Sdk/OsTypes')
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/Event.cs61
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs8
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/EventType.cs15
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs89
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs136
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs250
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs8
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs27
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs43
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs16
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs39
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs45
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs14
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs130
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs10
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs33
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs11
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs85
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs10
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs17
-rw-r--r--Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs9
21 files changed, 1056 insertions, 0 deletions
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Event.cs b/Ryujinx.Horizon/Sdk/OsTypes/Event.cs
new file mode 100644
index 00000000..79d7408e
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/Event.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ class Event : IDisposable
+ {
+ 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 TriBool IsSignaledThreadUnsafe()
+ {
+ 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()
+ {
+ 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/EventClearMode.cs b/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs
new file mode 100644
index 00000000..b500e6b3
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/EventClearMode.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ enum EventClearMode
+ {
+ ManualClear,
+ AutoClear
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs b/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs
new file mode 100644
index 00000000..b4b1a275
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/EventType.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ struct EventType
+ {
+ public LinkedList<MultiWaitHolderBase> MultiWaitHolders;
+ public bool Signaled;
+ public bool InitiallySignaled;
+ public EventClearMode ClearMode;
+ public InitializationState State;
+ public ulong BroadcastCounter;
+ public object Lock;
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs
new file mode 100644
index 00000000..62b5bf06
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEvent.cs
@@ -0,0 +1,89 @@
+using Ryujinx.Horizon.Common;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
+{
+ static class InterProcessEvent
+ {
+ public static Result Create(ref InterProcessEventType ipEvent, EventClearMode clearMode)
+ {
+ Result result = InterProcessEventImpl.Create(out int writableHandle, out int readableHandle);
+
+ if (result != Result.Success)
+ {
+ return result;
+ }
+
+ ipEvent = new InterProcessEventType(
+ clearMode == EventClearMode.AutoClear,
+ true,
+ true,
+ readableHandle,
+ writableHandle);
+
+ return Result.Success;
+ }
+
+ public static void Destroy(ref InterProcessEventType ipEvent)
+ {
+ ipEvent.State = InitializationState.NotInitialized;
+
+ if (ipEvent.ReadableHandleManaged)
+ {
+ if (ipEvent.ReadableHandle != 0)
+ {
+ InterProcessEventImpl.Close(ipEvent.ReadableHandle);
+ }
+ ipEvent.ReadableHandleManaged = false;
+ }
+
+ if (ipEvent.WritableHandleManaged)
+ {
+ if (ipEvent.WritableHandle != 0)
+ {
+ InterProcessEventImpl.Close(ipEvent.WritableHandle);
+ }
+ ipEvent.WritableHandleManaged = false;
+ }
+ }
+
+ public static int DetachReadableHandle(ref InterProcessEventType ipEvent)
+ {
+ int handle = ipEvent.ReadableHandle;
+
+ ipEvent.ReadableHandle = 0;
+ ipEvent.ReadableHandleManaged = false;
+
+ return handle;
+ }
+
+ public static int DetachWritableHandle(ref InterProcessEventType ipEvent)
+ {
+ int handle = ipEvent.WritableHandle;
+
+ ipEvent.WritableHandle = 0;
+ ipEvent.WritableHandleManaged = false;
+
+ return handle;
+ }
+
+ public static int GetReadableHandle(ref InterProcessEventType ipEvent)
+ {
+ return ipEvent.ReadableHandle;
+ }
+
+ public static int GetWritableHandle(ref InterProcessEventType ipEvent)
+ {
+ return ipEvent.WritableHandle;
+ }
+
+ public static void Signal(ref InterProcessEventType ipEvent)
+ {
+ InterProcessEventImpl.Signal(ipEvent.WritableHandle);
+ }
+
+ public static void Clear(ref InterProcessEventType ipEvent)
+ {
+ InterProcessEventImpl.Clear(ipEvent.ReadableHandle == 0 ? ipEvent.WritableHandle : ipEvent.ReadableHandle);
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs b/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs
new file mode 100644
index 00000000..a8aeacc9
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/Impl/InterProcessEventImpl.cs
@@ -0,0 +1,136 @@
+using Ryujinx.Horizon.Common;
+using System;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
+{
+ static class InterProcessEventImpl
+ {
+ public static Result Create(out int writableHandle, out int readableHandle)
+ {
+ Result result = HorizonStatic.Syscall.CreateEvent(out writableHandle, out readableHandle);
+
+ if (result == KernelResult.OutOfResource)
+ {
+ return OsResult.OutOfResource;
+ }
+
+ result.AbortOnFailure();
+
+ return Result.Success;
+ }
+
+ public static void Close(int handle)
+ {
+ if (handle != 0)
+ {
+ HorizonStatic.Syscall.CloseHandle(handle).AbortOnFailure();
+ }
+ }
+
+ public static void Signal(int handle)
+ {
+ HorizonStatic.Syscall.SignalEvent(handle).AbortOnFailure();
+ }
+
+ public static void Clear(int handle)
+ {
+ HorizonStatic.Syscall.ClearEvent(handle).AbortOnFailure();
+ }
+
+ public static void Wait(int handle, bool autoClear)
+ {
+ Span<int> handles = stackalloc int[1];
+
+ handles[0] = handle;
+
+ while (true)
+ {
+ Result result = HorizonStatic.Syscall.WaitSynchronization(out _, handles, -1L);
+
+ if (result == Result.Success)
+ {
+ if (autoClear)
+ {
+ result = HorizonStatic.Syscall.ResetSignal(handle);
+
+ if (result == KernelResult.InvalidState)
+ {
+ continue;
+ }
+
+ result.AbortOnFailure();
+ }
+
+ return;
+ }
+
+ result.AbortUnless(KernelResult.Cancelled);
+ }
+ }
+
+ public static bool TryWait(int handle, bool autoClear)
+ {
+ if (autoClear)
+ {
+ return HorizonStatic.Syscall.ResetSignal(handle) == Result.Success;
+ }
+
+ Span<int> handles = stackalloc int[1];
+
+ handles[0] = handle;
+
+ while (true)
+ {
+ Result result = HorizonStatic.Syscall.WaitSynchronization(out _, handles, 0);
+
+ if (result == Result.Success)
+ {
+ return true;
+ }
+ else if (result == KernelResult.TimedOut)
+ {
+ return false;
+ }
+
+ result.AbortUnless(KernelResult.Cancelled);
+ }
+ }
+
+ public static bool TimedWait(int handle, bool autoClear, TimeSpan timeout)
+ {
+ Span<int> handles = stackalloc int[1];
+
+ handles[0] = handle;
+
+ long timeoutNs = timeout.Milliseconds * 1000000L;
+
+ while (true)
+ {
+ Result result = HorizonStatic.Syscall.WaitSynchronization(out _, handles, timeoutNs);
+
+ if (result == Result.Success)
+ {
+ if (autoClear)
+ {
+ result = HorizonStatic.Syscall.ResetSignal(handle);
+
+ if (result == KernelResult.InvalidState)
+ {
+ continue;
+ }
+
+ result.AbortOnFailure();
+ }
+
+ return true;
+ }
+ else if (result == KernelResult.TimedOut)
+ {
+ return false;
+ }
+
+ result.AbortUnless(KernelResult.Cancelled);
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs b/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs
new file mode 100644
index 00000000..fd45792d
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/Impl/MultiWaitImpl.cs
@@ -0,0 +1,250 @@
+using Ryujinx.Common;
+using Ryujinx.Horizon.Common;
+using System.Collections.Generic;
+using System;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
+{
+ class MultiWaitImpl
+ {
+ private const int WaitTimedOut = -1;
+ private const int WaitCancelled = -2;
+ private const int WaitInvalid = -3;
+
+ private readonly List<MultiWaitHolderBase> _multiWaits;
+
+ private object _lock;
+
+ private int _waitingThreadHandle;
+
+ private MultiWaitHolderBase _signaledHolder;
+
+ public long CurrentTime { get; private set; }
+
+ public MultiWaitImpl()
+ {
+ _multiWaits = new List<MultiWaitHolderBase>();
+
+ _lock = new object();
+ }
+
+ public void LinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder)
+ {
+ _multiWaits.Add(multiWaitHolder);
+ }
+
+ public void UnlinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder)
+ {
+ _multiWaits.Remove(multiWaitHolder);
+ }
+
+ public void MoveAllFrom(MultiWaitImpl other)
+ {
+ foreach (MultiWaitHolderBase multiWait in other._multiWaits)
+ {
+ multiWait.SetMultiWait(this);
+ }
+
+ _multiWaits.AddRange(other._multiWaits);
+
+ other._multiWaits.Clear();
+ }
+
+ public MultiWaitHolderBase WaitAnyImpl(bool infinite, long timeout)
+ {
+ _signaledHolder = null;
+ _waitingThreadHandle = Os.GetCurrentThreadHandle();
+
+ MultiWaitHolderBase result = LinkHoldersToObjectList();
+
+ lock (_lock)
+ {
+ if (_signaledHolder != null)
+ {
+ result = _signaledHolder;
+ }
+ }
+
+ if (result == null)
+ {
+ result = WaitAnyHandleImpl(infinite, timeout);
+ }
+
+ UnlinkHoldersFromObjectsList();
+ _waitingThreadHandle = 0;
+
+ return result;
+ }
+
+ private MultiWaitHolderBase WaitAnyHandleImpl(bool infinite, long timeout)
+ {
+ Span<int> objectHandles = new int[64];
+
+ Span<MultiWaitHolderBase> objects = new MultiWaitHolderBase[64];
+
+ int count = FillObjectsArray(objectHandles, objects);
+
+ long endTime = infinite ? long.MaxValue : PerformanceCounter.ElapsedMilliseconds * 1000000;
+
+ while (true)
+ {
+ CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000;
+
+ MultiWaitHolderBase minTimeoutObject = RecalcMultiWaitTimeout(endTime, out long minTimeout);
+
+ int index;
+
+ if (count == 0 && minTimeout == 0)
+ {
+ index = WaitTimedOut;
+ }
+ else
+ {
+ index = WaitSynchronization(objectHandles.Slice(0, count), minTimeout);
+
+ DebugUtil.Assert(index != WaitInvalid);
+ }
+
+ switch (index)
+ {
+ case WaitTimedOut:
+ if (minTimeoutObject != null)
+ {
+ CurrentTime = PerformanceCounter.ElapsedMilliseconds * 1000000;
+
+ if (minTimeoutObject.Signaled == TriBool.True)
+ {
+ lock (_lock)
+ {
+ _signaledHolder = minTimeoutObject;
+
+ return _signaledHolder;
+ }
+ }
+ }
+ else
+ {
+ return null;
+ }
+ break;
+ case WaitCancelled:
+ lock (_lock)
+ {
+ if (_signaledHolder != null)
+ {
+ return _signaledHolder;
+ }
+ }
+ break;
+ default:
+ lock (_lock)
+ {
+ _signaledHolder = objects[index];
+
+ return _signaledHolder;
+ }
+ }
+ }
+ }
+
+ private int FillObjectsArray(Span<int> handles, Span<MultiWaitHolderBase> objects)
+ {
+ int count = 0;
+
+ foreach (MultiWaitHolderBase holder in _multiWaits)
+ {
+ int handle = holder.Handle;
+
+ if (handle != 0)
+ {
+ handles[count] = handle;
+ objects[count] = holder;
+
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ private MultiWaitHolderBase RecalcMultiWaitTimeout(long endTime, out long minTimeout)
+ {
+ MultiWaitHolderBase minTimeHolder = null;
+
+ long minTime = endTime;
+
+ foreach (MultiWaitHolder holder in _multiWaits)
+ {
+ long currentTime = holder.GetAbsoluteTimeToWakeup();
+
+ if ((ulong)currentTime < (ulong)minTime)
+ {
+ minTimeHolder = holder;
+
+ minTime = currentTime;
+ }
+ }
+
+ minTimeout = (ulong)minTime < (ulong)CurrentTime ? 0 : minTime - CurrentTime;
+
+ return minTimeHolder;
+ }
+
+ private static int WaitSynchronization(ReadOnlySpan<int> handles, long timeout)
+ {
+ Result result = HorizonStatic.Syscall.WaitSynchronization(out int index, handles, timeout);
+
+ if (result == KernelResult.TimedOut)
+ {
+ return WaitTimedOut;
+ }
+ else if (result == KernelResult.Cancelled)
+ {
+ return WaitCancelled;
+ }
+ else
+ {
+ result.AbortOnFailure();
+ }
+
+ 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;
+
+ foreach (MultiWaitHolderBase holder in _multiWaits)
+ {
+ TriBool isSignaled = holder.LinkToObjectList();
+
+ if (signaledHolder == null && isSignaled == TriBool.True)
+ {
+ signaledHolder = holder;
+ }
+ }
+
+ return signaledHolder;
+ }
+
+ private void UnlinkHoldersFromObjectsList()
+ {
+ foreach (MultiWaitHolderBase holder in _multiWaits)
+ {
+ holder.UnlinkFromObjectList();
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs b/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs
new file mode 100644
index 00000000..45ffd258
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/InitializationState.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ enum InitializationState : byte
+ {
+ NotInitialized,
+ Initialized
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs b/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs
new file mode 100644
index 00000000..5f6824fe
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/InterProcessEventType.cs
@@ -0,0 +1,27 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ struct InterProcessEventType
+ {
+ public readonly bool AutoClear;
+ public InitializationState State;
+ public bool ReadableHandleManaged;
+ public bool WritableHandleManaged;
+ public int ReadableHandle;
+ public int WritableHandle;
+
+ public InterProcessEventType(
+ bool autoClear,
+ bool readableHandleManaged,
+ bool writableHandleManaged,
+ int readableHandle,
+ int writableHandle)
+ {
+ AutoClear = autoClear;
+ State = InitializationState.Initialized;
+ ReadableHandleManaged = readableHandleManaged;
+ WritableHandleManaged = writableHandleManaged;
+ ReadableHandle = readableHandle;
+ WritableHandle = writableHandle;
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs
new file mode 100644
index 00000000..5a91f6c3
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWait.cs
@@ -0,0 +1,43 @@
+using Ryujinx.Horizon.Sdk.OsTypes.Impl;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ class MultiWait
+ {
+ private readonly MultiWaitImpl _impl;
+
+ public MultiWait()
+ {
+ _impl = new MultiWaitImpl();
+ }
+
+ public void LinkMultiWaitHolder(MultiWaitHolderBase multiWaitHolder)
+ {
+ DebugUtil.Assert(!multiWaitHolder.IsLinked);
+
+ _impl.LinkMultiWaitHolder(multiWaitHolder);
+
+ multiWaitHolder.SetMultiWait(_impl);
+ }
+
+ public void MoveAllFrom(MultiWait other)
+ {
+ _impl.MoveAllFrom(other._impl);
+ }
+
+ public MultiWaitHolder WaitAny()
+ {
+ return (MultiWaitHolder)_impl.WaitAnyImpl(true, -1L);
+ }
+
+ public MultiWaitHolder TryWaitAny()
+ {
+ return (MultiWaitHolder)_impl.WaitAnyImpl(false, 0);
+ }
+
+ public MultiWaitHolder TimedWaitAny(long timeout)
+ {
+ return (MultiWaitHolder)_impl.WaitAnyImpl(false, timeout);
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs
new file mode 100644
index 00000000..a24b1906
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolder.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ class MultiWaitHolder : MultiWaitHolderBase
+ {
+ public object UserData { get; set; }
+
+ public void UnlinkFromMultiWaitHolder()
+ {
+ DebugUtil.Assert(IsLinked);
+
+ MultiWait.UnlinkMultiWaitHolder(this);
+
+ SetMultiWait(null);
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs
new file mode 100644
index 00000000..018305ba
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderBase.cs
@@ -0,0 +1,39 @@
+using Ryujinx.Horizon.Sdk.OsTypes.Impl;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ class MultiWaitHolderBase
+ {
+ protected MultiWaitImpl MultiWait;
+
+ public bool IsLinked => MultiWait != null;
+
+ public virtual TriBool Signaled => TriBool.False;
+
+ public virtual int Handle => 0;
+
+ public void SetMultiWait(MultiWaitImpl multiWait)
+ {
+ MultiWait = multiWait;
+ }
+
+ public MultiWaitImpl GetMultiWait()
+ {
+ return MultiWait;
+ }
+
+ public virtual TriBool LinkToObjectList()
+ {
+ return TriBool.Undefined;
+ }
+
+ 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 00000000..37ac22f0
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfEvent.cs
@@ -0,0 +1,45 @@
+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;
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs
new file mode 100644
index 00000000..6fc5c75b
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/MultiWaitHolderOfHandle.cs
@@ -0,0 +1,14 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ class MultiWaitHolderOfHandle : MultiWaitHolder
+ {
+ private int _handle;
+
+ public override int Handle => _handle;
+
+ public MultiWaitHolderOfHandle(int handle)
+ {
+ _handle = handle;
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs
new file mode 100644
index 00000000..cc7e8483
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/OsEvent.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ static partial class Os
+ {
+ public static void InitializeEvent(out EventType evnt, bool signaled, EventClearMode clearMode)
+ {
+ evnt = new EventType
+ {
+ MultiWaitHolders = new LinkedList<MultiWaitHolderBase>(),
+ Signaled = signaled,
+ InitiallySignaled = signaled,
+ 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/OsMultiWait.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs
new file mode 100644
index 00000000..827de231
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/OsMultiWait.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ static partial class Os
+ {
+ public static void FinalizeMultiWaitHolder(MultiWaitHolderBase holder)
+ {
+ DebugUtil.Assert(!holder.IsLinked);
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs
new file mode 100644
index 00000000..6a6d9bf2
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/OsProcessHandle.cs
@@ -0,0 +1,33 @@
+using Ryujinx.Horizon.Common;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ static partial class Os
+ {
+ private const int SelfProcessHandle = (0x1ffff << 15) | 1;
+
+ public static int GetCurrentProcessHandle()
+ {
+ return SelfProcessHandle;
+ }
+
+ public static ulong GetCurrentProcessId()
+ {
+ return GetProcessId(GetCurrentProcessHandle());
+ }
+
+ private static ulong GetProcessId(int handle)
+ {
+ Result result = TryGetProcessId(handle, out ulong pid);
+
+ result.AbortOnFailure();
+
+ return pid;
+ }
+
+ private static Result TryGetProcessId(int handle, out ulong pid)
+ {
+ return HorizonStatic.Syscall.GetProcessId(out pid, handle);
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs
new file mode 100644
index 00000000..86dcd1fa
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/OsResult.cs
@@ -0,0 +1,11 @@
+using Ryujinx.Horizon.Common;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ static class OsResult
+ {
+ private const int ModuleId = 3;
+
+ public static Result OutOfResource => new Result(ModuleId, 9);
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs b/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs
new file mode 100644
index 00000000..061d7a3c
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/OsSystemEvent.cs
@@ -0,0 +1,85 @@
+using Ryujinx.Horizon.Sdk.OsTypes.Impl;
+using Ryujinx.Horizon.Common;
+using System;
+
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ static partial class Os
+ {
+ public static Result CreateSystemEvent(out SystemEventType sysEvent, EventClearMode clearMode, bool interProcess)
+ {
+ sysEvent = new SystemEventType();
+
+ if (interProcess)
+ {
+ Result result = InterProcessEvent.Create(ref sysEvent.InterProcessEvent, clearMode);
+
+ if (result != Result.Success)
+ {
+ return result;
+ }
+
+ sysEvent.State = SystemEventType.InitializationState.InitializedAsInterProcess;
+ }
+ else
+ {
+ throw new NotImplementedException();
+ }
+
+ return Result.Success;
+ }
+
+ public static void DestroySystemEvent(ref SystemEventType sysEvent)
+ {
+ var oldState = sysEvent.State;
+ sysEvent.State = SystemEventType.InitializationState.NotInitialized;
+
+ switch (oldState)
+ {
+ case SystemEventType.InitializationState.InitializedAsInterProcess:
+ InterProcessEvent.Destroy(ref sysEvent.InterProcessEvent);
+ break;
+ }
+ }
+
+ public static int DetachReadableHandleOfSystemEvent(ref SystemEventType sysEvent)
+ {
+ return InterProcessEvent.DetachReadableHandle(ref sysEvent.InterProcessEvent);
+ }
+
+ public static int DetachWritableHandleOfSystemEvent(ref SystemEventType sysEvent)
+ {
+ return InterProcessEvent.DetachWritableHandle(ref sysEvent.InterProcessEvent);
+ }
+
+ public static int GetReadableHandleOfSystemEvent(ref SystemEventType sysEvent)
+ {
+ return InterProcessEvent.GetReadableHandle(ref sysEvent.InterProcessEvent);
+ }
+
+ public static int GetWritableHandleOfSystemEvent(ref SystemEventType sysEvent)
+ {
+ return InterProcessEvent.GetWritableHandle(ref sysEvent.InterProcessEvent);
+ }
+
+ public static void SignalSystemEvent(ref SystemEventType sysEvent)
+ {
+ switch (sysEvent.State)
+ {
+ case SystemEventType.InitializationState.InitializedAsInterProcess:
+ InterProcessEvent.Signal(ref sysEvent.InterProcessEvent);
+ break;
+ }
+ }
+
+ public static void ClearSystemEvent(ref SystemEventType sysEvent)
+ {
+ switch (sysEvent.State)
+ {
+ 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
new file mode 100644
index 00000000..2037cd7f
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/OsThreadManager.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ static partial class Os
+ {
+ public static int GetCurrentThreadHandle()
+ {
+ return HorizonStatic.CurrentThreadHandle;
+ }
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs b/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs
new file mode 100644
index 00000000..338493d2
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/SystemEventType.cs
@@ -0,0 +1,17 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ struct SystemEventType
+ {
+ public enum InitializationState : byte
+ {
+ NotInitialized,
+ InitializedAsEvent,
+ InitializedAsInterProcess
+ }
+
+ public InterProcessEventType InterProcessEvent;
+ public InitializationState State;
+
+ public bool NotInitialized => State == InitializationState.NotInitialized;
+ }
+}
diff --git a/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs b/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs
new file mode 100644
index 00000000..7debd9e2
--- /dev/null
+++ b/Ryujinx.Horizon/Sdk/OsTypes/TriBool.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Horizon.Sdk.OsTypes
+{
+ enum TriBool
+ {
+ False,
+ True,
+ Undefined
+ }
+}