Add os::Semaphore

This commit is contained in:
Alex Barney 2022-04-11 15:03:27 -07:00
parent e46c1f0231
commit 7dd137da6a
5 changed files with 260 additions and 8 deletions

View file

@ -1,13 +1,15 @@
using System.Threading;
using System;
using System.Threading;
using LibHac.Diag;
namespace LibHac.Os.Impl;
internal struct InternalConditionVariableImpl
internal struct InternalConditionVariableImpl : IDisposable
{
private object _obj;
public InternalConditionVariableImpl() => _obj = new object();
public void Dispose() { }
public void Initialize() => _obj = new object();
public void Signal()

View file

@ -1,6 +1,8 @@
namespace LibHac.Os.Impl;
using System;
internal struct InternalConditionVariable
namespace LibHac.Os.Impl;
internal struct InternalConditionVariable : IDisposable
{
private InternalConditionVariableImpl _impl;
@ -9,6 +11,11 @@ internal struct InternalConditionVariable
_impl = new InternalConditionVariableImpl();
}
public void Dispose()
{
_impl.Dispose();
}
public void Initialize() => _impl.Initialize();
public void Signal() => _impl.Signal();
public void Broadcast() => _impl.Broadcast();

View file

@ -6,9 +6,8 @@ public class MultiWaitHolderImpl
public MultiWaitHolderBase HolderBase => _holder;
public MultiWaitHolderOfNativeHandle HolderOfNativeHandle => (MultiWaitHolderOfNativeHandle)_holder;
public MultiWaitHolderOfSemaphore HolderOfSemaphore => (MultiWaitHolderOfSemaphore)_holder;
public MultiWaitHolderImpl(MultiWaitHolderOfNativeHandle holder)
{
_holder = holder;
}
public MultiWaitHolderImpl(MultiWaitHolderOfNativeHandle holder) => _holder = holder;
public MultiWaitHolderImpl(MultiWaitHolderOfSemaphore holder) => _holder = holder;
}

View file

@ -0,0 +1,38 @@
namespace LibHac.Os.Impl;
public class MultiWaitHolderOfSemaphore : MultiWaitHolderOfUserWaitObject
{
private Semaphore _semaphore;
public MultiWaitHolderOfSemaphore(Semaphore semaphore)
{
_semaphore = semaphore;
}
public override TriBool IsSignaled()
{
using ScopedLock<InternalCriticalSection> lk = ScopedLock.Lock(ref _semaphore.GetBase().CsSemaphore);
return IsSignaledUnsafe();
}
public override TriBool AddToObjectList()
{
using ScopedLock<InternalCriticalSection> lk = ScopedLock.Lock(ref _semaphore.GetBase().CsSemaphore);
_semaphore.GetBase().WaitList.PushBackToList(this);
return IsSignaledUnsafe();
}
public override void RemoveFromObjectList()
{
using ScopedLock<InternalCriticalSection> lk = ScopedLock.Lock(ref _semaphore.GetBase().CsSemaphore);
_semaphore.GetBase().WaitList.EraseFromList(this);
}
private TriBool IsSignaledUnsafe()
{
return _semaphore.GetBase().Count > 0 ? TriBool.True : TriBool.False;
}
}

206
src/LibHac/Os/Semaphore.cs Normal file
View file

@ -0,0 +1,206 @@
using System;
using LibHac.Diag;
using LibHac.Os.Impl;
namespace LibHac.Os;
public class Semaphore : IDisposable
{
private SemaphoreType _semaphore;
public Semaphore(OsState os, int initialCount, int maxCount) =>
_semaphore = new SemaphoreType(os, initialCount, maxCount);
public void Dispose() => _semaphore.Dispose();
public void Acquire() => _semaphore.Acquire();
public bool TryAcquire() => _semaphore.TryAcquire();
public bool TimedAcquire(TimeSpan timeout) => _semaphore.TimedAcquire(timeout);
public void Release() => _semaphore.Release();
public void Release(int count) => _semaphore.Release(count);
public int GetCurrentCount() => _semaphore.GetCurrentCount();
public ref SemaphoreType GetBase() => ref _semaphore;
}
public struct SemaphoreType : IDisposable
{
public enum State : byte
{
NotInitialized = 0,
Initialized = 1
}
internal MultiWaitObjectList WaitList;
internal State CurrentState;
internal int Count;
internal int MaxCount;
internal InternalCriticalSection CsSemaphore;
internal InternalConditionVariable CvNotZero;
private readonly OsState _os;
public SemaphoreType(OsState os, int initialCount, int maxCount)
{
Assert.SdkRequires(maxCount >= 1);
Assert.SdkRequires(initialCount >= 0 && initialCount <= maxCount);
CsSemaphore = new InternalCriticalSection();
CvNotZero = new InternalConditionVariable();
WaitList = new MultiWaitObjectList();
Count = initialCount;
MaxCount = maxCount;
CurrentState = State.Initialized;
_os = os;
}
public void Dispose()
{
Assert.SdkRequires(CurrentState == State.Initialized);
Assert.SdkRequires(WaitList.IsEmpty());
CurrentState = State.NotInitialized;
CsSemaphore.Dispose();
CvNotZero.Dispose();
}
public void Acquire()
{
Assert.SdkRequires(CurrentState == State.Initialized);
using ScopedLock<InternalCriticalSection> lk = ScopedLock.Lock(ref CsSemaphore);
while (Count == 0)
{
CvNotZero.Wait(ref CsSemaphore);
}
Count--;
}
public bool TryAcquire()
{
Assert.SdkRequires(CurrentState == State.Initialized);
using ScopedLock<InternalCriticalSection> lk = ScopedLock.Lock(ref CsSemaphore);
if (Count == 0)
{
return false;
}
Count--;
return true;
}
public bool TimedAcquire(TimeSpan timeout)
{
Assert.SdkRequires(CurrentState == State.Initialized);
Assert.SdkRequires(timeout.GetNanoSeconds() >= 0);
var timeoutHelper = new TimeoutHelper(_os, timeout);
using ScopedLock<InternalCriticalSection> lk = ScopedLock.Lock(ref CsSemaphore);
while (Count == 0)
{
if (timeoutHelper.TimedOut())
return false;
CvNotZero.TimedWait(ref CsSemaphore, in timeoutHelper);
}
Count--;
return true;
}
public void Release()
{
Assert.SdkRequires(CurrentState == State.Initialized);
using ScopedLock<InternalCriticalSection> lk = ScopedLock.Lock(ref CsSemaphore);
Assert.SdkAssert(Count + 1 <= MaxCount);
Count++;
CvNotZero.Signal();
WaitList.WakeupAllMultiWaitThreadsUnsafe();
}
public void Release(int count)
{
Assert.SdkRequires(CurrentState == State.Initialized);
Assert.SdkRequires(Count >= 1);
using ScopedLock<InternalCriticalSection> lk = ScopedLock.Lock(ref CsSemaphore);
Assert.SdkAssert(Count + count <= MaxCount);
Count += count;
CvNotZero.Broadcast();
WaitList.WakeupAllMultiWaitThreadsUnsafe();
}
public readonly int GetCurrentCount()
{
Assert.SdkRequires(CurrentState == State.Initialized);
return Count;
}
}
public static class SemaphoreApi
{
public static void InitializeSemaphore(this OsState os, out SemaphoreType semaphore, int initialCount, int maxCount)
{
semaphore = new SemaphoreType(os, initialCount, maxCount);
}
public static void FinalizeSemaphore(this OsState os, ref SemaphoreType semaphore)
{
semaphore.Dispose();
}
public static void AcquireSemaphore(this OsState os, ref SemaphoreType semaphore)
{
semaphore.Acquire();
}
public static bool TryAcquireSemaphore(this OsState os, ref SemaphoreType semaphore)
{
return semaphore.TryAcquire();
}
public static bool TimedAcquireSemaphore(this OsState os, ref SemaphoreType semaphore, TimeSpan timeout)
{
return semaphore.TimedAcquire(timeout);
}
public static void ReleaseSemaphore(this OsState os, ref SemaphoreType semaphore)
{
semaphore.Release();
}
public static void ReleaseSemaphore(this OsState os, ref SemaphoreType semaphore, int count)
{
semaphore.Release(count);
}
public static int GetCurrentSemaphoreCount(this OsState os, in SemaphoreType semaphore)
{
return semaphore.GetCurrentCount();
}
public static void InitializeMultiWaitHolder(this OsState os, MultiWaitHolderType holder, Semaphore semaphore)
{
Assert.SdkRequires(semaphore.GetBase().CurrentState == SemaphoreType.State.Initialized);
holder.Impl = new MultiWaitHolderImpl(new MultiWaitHolderOfSemaphore(semaphore));
holder.UserData = null;
}
}