add cancellable time span await
This commit is contained in:
@@ -43,7 +43,8 @@ namespace StgSharp.HighPerformance
|
|||||||
public M64()
|
public M64()
|
||||||
{
|
{
|
||||||
Unsafe.SkipInit(out this);
|
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>
|
||||||
@@ -77,28 +78,10 @@ namespace StgSharp.HighPerformance
|
|||||||
return !(left == right);
|
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)
|
public static bool operator ==(M64 left, M64 right)
|
||||||
{
|
{
|
||||||
return left.value == right.value;
|
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()
|
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);
|
s_unusedID.Push(TokenID);
|
||||||
_event.Dispose();
|
_event.Dispose();
|
||||||
GC.SuppressFinalize(this);
|
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.
|
internal void MissRefresh() => _missHandler?.Invoke(this, EventArgs.Empty); // Legacy compatibility.
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -163,13 +209,16 @@ namespace StgSharp.Timing
|
|||||||
{
|
{
|
||||||
if (_sequence.EndsAt(currentTick))
|
if (_sequence.EndsAt(currentTick))
|
||||||
{
|
{
|
||||||
|
// mark ended due to sequence timeout, then dispose
|
||||||
|
Volatile.Write(ref _endedFlag, 1);
|
||||||
Dispose();
|
Dispose();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!_sequence.IsNextFrameReady(currentTick)) {
|
if (!_sequence.IsNextFrameReady(currentTick)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Console.WriteLine("Frame hit!");
|
|
||||||
|
// Console.WriteLine("Frame hit!");
|
||||||
|
|
||||||
// Fast path: if no pending consumers, initialize the counter to current capacity (_size).
|
// Fast path: if no pending consumers, initialize the counter to current capacity (_size).
|
||||||
if (Volatile.Read(ref _pendingSpans) == 0)
|
if (Volatile.Read(ref _pendingSpans) == 0)
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ namespace StgSharp.Timing
|
|||||||
|
|
||||||
private bool _subscribed;
|
private bool _subscribed;
|
||||||
private int _emittedSpanCount; // legacy counter
|
private int _emittedSpanCount; // legacy counter
|
||||||
|
|
||||||
|
// internal ended flag (0 = running, 1 = ended)
|
||||||
|
private int _endedFlag = 0;
|
||||||
private readonly TimeSequence _sequence;
|
private readonly TimeSequence _sequence;
|
||||||
private readonly TimeSourceProviderBase _timeSource;
|
private readonly TimeSourceProviderBase _timeSource;
|
||||||
|
|
||||||
@@ -62,6 +65,11 @@ namespace StgSharp.Timing
|
|||||||
_pendingSpans = 0;
|
_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
|
public double CurrentSecond
|
||||||
{
|
{
|
||||||
get => (double)_sequence.PreviousFrameTick / _timeSource.Frequency;
|
get => (double)_sequence.PreviousFrameTick / _timeSource.Frequency;
|
||||||
|
|||||||
Reference in New Issue
Block a user