add cancellable time span await

This commit is contained in:
Youmu Liu
2025-11-21 18:11:52 +08:00
parent 98f47aaa53
commit 61fbce14a2
3 changed files with 63 additions and 23 deletions
+5 -22
View File
@@ -43,15 +43,16 @@ namespace StgSharp.HighPerformance
public M64()
{
Unsafe.SkipInit(out this);
value = 0;
Unsafe.SkipInit(out vec);
Unsafe.SkipInit(out value);
}
public ref T AsRef<T>() where T: unmanaged,INumber<T>
public ref T AsRef<T>() where T : unmanaged,INumber<T>
{
return ref Unsafe.As<byte, T>(ref buffer[0]);
}
public void BroadCastFrom<T>(T value) where T: unmanaged, INumber<T>
public void BroadCastFrom<T>(T value) where T : unmanaged, INumber<T>
{
vec = Vector64.Create(value).AsSingle();
}
@@ -67,7 +68,7 @@ namespace StgSharp.HighPerformance
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T Member<T>(int index) where T: unmanaged, INumber<T>
public ref T Member<T>(int index) where T : unmanaged, INumber<T>
{
return ref Unsafe.As<byte, T>(ref buffer[index * sizeof(T)]);
}
@@ -77,28 +78,10 @@ namespace StgSharp.HighPerformance
return !(left == right);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static M64 operator <<(M64 m, int shift)
{
return new M64
{
value = m.value << shift
};
}
public static bool operator ==(M64 left, M64 right)
{
return left.value == right.value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static M64 operator >>(M64 m, int shift)
{
return new M64
{
value = m.value >> shift
};
}
}
}
@@ -85,6 +85,12 @@ namespace StgSharp.Timing
public void Dispose()
{
// mark provider as ended so external users can observe terminal state
try
{
Volatile.Write(ref _endedFlag, 1);
}
catch { }
s_unusedID.Push(TokenID);
_event.Dispose();
GC.SuppressFinalize(this);
@@ -147,6 +153,46 @@ namespace StgSharp.Timing
}
}
/// <summary>
/// Wait for the next timespan tick, but honor the provided cancellation token. Throws
/// OperationCanceledException when token is cancelled. If the provider is disposed during
/// wait an OperationCanceledException is thrown to indicate end.
/// </summary>
public Task WaitNextSpanAsync(CancellationToken ct)
{
try
{
// Block the current thread until the event is signaled or cancellation requested.
_event.Wait(ct);
}
catch (ObjectDisposedException)
{
// provider disposed while waiting - treat as cancellation/ended
throw new OperationCanceledException(ct);
}
// consume one pending span (same logic as non-cancellable variant)
while (true)
{
int cur = Volatile.Read(ref _pendingSpans);
if (cur == 0)
{
break;
}
if (Interlocked.CompareExchange(ref _pendingSpans, cur - 1, cur) == cur)
{
if (cur - 1 == 0) {
_event.Reset();
}
break;
}
}
return Task.CompletedTask;
}
internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
/// <summary>
@@ -163,13 +209,16 @@ namespace StgSharp.Timing
{
if (_sequence.EndsAt(currentTick))
{
// mark ended due to sequence timeout, then dispose
Volatile.Write(ref _endedFlag, 1);
Dispose();
return false;
}
if (!_sequence.IsNextFrameReady(currentTick)) {
return true;
}
Console.WriteLine("Frame hit!");
// Console.WriteLine("Frame hit!");
// Fast path: if no pending consumers, initialize the counter to current capacity (_size).
if (Volatile.Read(ref _pendingSpans) == 0)
@@ -41,6 +41,9 @@ namespace StgSharp.Timing
private bool _subscribed;
private int _emittedSpanCount; // legacy counter
// internal ended flag (0 = running, 1 = ended)
private int _endedFlag = 0;
private readonly TimeSequence _sequence;
private readonly TimeSourceProviderBase _timeSource;
@@ -62,6 +65,11 @@ namespace StgSharp.Timing
_pendingSpans = 0;
}
/// <summary>
/// True when the provider has reached its end and will not emit further spans.
/// </summary>
public bool IsEnded => Volatile.Read(ref _endedFlag) != 0;
public double CurrentSecond
{
get => (double)_sequence.PreviousFrameTick / _timeSource.Frequency;