Skip to content

Commit f79c098

Browse files
[S.N.Quic] Observe exceptions in ResettableValueTaskSource (#114226)
* Observe exceptions in ResettableValueTaskSource * Update src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ResettableValueTaskSource.cs Co-authored-by: Marie Píchová <[email protected]> --------- Co-authored-by: Marie Píchová <[email protected]>
1 parent bb418ad commit f79c098

File tree

2 files changed

+31
-26
lines changed

2 files changed

+31
-26
lines changed

src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/ResettableValueTaskSource.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public ResettableValueTaskSource()
5252
public Action<object?> CancellationAction { init { _cancellationAction = value; } }
5353

5454
/// <summary>
55-
/// Returns <c>true</c> is this task source has entered its final state, i.e. <see cref="TrySetResult(bool)"/> or <see cref="TrySetException(Exception, bool)"/>
55+
/// Returns <c>true</c> is this task source has entered its final state, i.e. <see cref="TrySetResult(bool)"/> or <see cref="TrySetException(Exception)"/>
5656
/// was called with <c>final</c> set to <c>true</c> and the result was propagated.
5757
/// </summary>
5858
public bool IsCompleted => (State)Volatile.Read(ref Unsafe.As<State, byte>(ref _state)) == State.Completed;
@@ -173,6 +173,7 @@ private bool TryComplete(Exception? exception, bool final)
173173
// Unblock the current task source and in case of a final also the final task source.
174174
if (exception is not null)
175175
{
176+
Debug.Assert(final);
176177
// Set up the exception stack trace for the caller.
177178
exception = exception.StackTrace is null ? ExceptionDispatchInfo.SetCurrentStackTrace(exception) : exception;
178179
if (state is State.None or State.Awaiting)
@@ -192,7 +193,7 @@ private bool TryComplete(Exception? exception, bool final)
192193
if (_finalTaskSource.TryComplete(exception))
193194
{
194195
// Signal the final task only if we don't have another result in the value task source.
195-
// In that case, the final task will be signalled after the value task result is retrieved.
196+
// In that case, the final task will be signaled after the value task result is retrieved.
196197
if (state != State.Ready)
197198
{
198199
_finalTaskSource.TrySignal(out _);
@@ -226,15 +227,14 @@ public bool TrySetResult(bool final = false)
226227
}
227228

228229
/// <summary>
229-
/// Tries to transition from <see cref="State.Awaiting"/> to either <see cref="State.Ready"/> or <see cref="State.Completed"/>, depending on the value of <paramref name="final"/>.
230-
/// Only the first call is able to do that with the exception of <c>TrySetResult()</c> followed by <c>TrySetResult(true)</c>, which will both return <c>true</c>.
230+
/// Tries to transition from <see cref="State.Awaiting"/> to <see cref="State.Completed"/>, setting an exception.
231+
/// Only the first call is able to do that.
231232
/// </summary>
232-
/// <param name="final">Whether this is the final transition to <see cref="State.Completed" /> or just a transition into <see cref="State.Ready"/> from which the task source can be reset back to <see cref="State.None"/>.</param>
233233
/// <param name="exception">The exception to set as a result of the value task.</param>
234234
/// <returns><c>true</c> if this is the first call that set the result; otherwise, <c>false</c>.</returns>
235-
public bool TrySetException(Exception exception, bool final = false)
235+
public bool TrySetException(Exception exception)
236236
{
237-
return TryComplete(exception, final);
237+
return TryComplete(exception, final: true);
238238
}
239239

240240
ValueTaskSourceStatus IValueTaskSource.GetStatus(short token)
@@ -297,6 +297,7 @@ private struct FinalTaskSource
297297
private bool _isCompleted;
298298
private bool _isSignaled;
299299
private Exception? _exception;
300+
private Task? _signaledTask;
300301

301302
public FinalTaskSource()
302303
{
@@ -312,9 +313,9 @@ public Task GetTask(object? keepAlive)
312313
{
313314
if (_isSignaled)
314315
{
315-
return _exception is null
316-
? Task.CompletedTask
317-
: Task.FromException(_exception);
316+
_signaledTask ??= _exception is null ? Task.CompletedTask : Task.FromException(_exception);
317+
_ = _signaledTask.Exception; // Observe the exception.
318+
return _signaledTask;
318319
}
319320

320321
_finalTaskSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
@@ -350,13 +351,17 @@ public bool TrySignal(out Exception? exception)
350351
return false;
351352
}
352353

353-
if (_exception is not null)
354+
if (_finalTaskSource is not null)
354355
{
355-
_finalTaskSource?.SetException(_exception);
356-
}
357-
else
358-
{
359-
_finalTaskSource?.SetResult();
356+
if (_exception is not null)
357+
{
358+
_finalTaskSource.SetException(_exception);
359+
_ = _finalTaskSource.Task.Exception; // Observe the exception.
360+
}
361+
else
362+
{
363+
_finalTaskSource.SetResult();
364+
}
360365
}
361366

362367
exception = _exception;

src/libraries/System.Net.Quic/src/System/Net/Quic/QuicStream.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ public ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, bool completeWrites, Ca
430430
exception = Volatile.Read(ref _sendException);
431431
if (exception is not null)
432432
{
433-
_sendTcs.TrySetException(exception, final: true);
433+
_sendTcs.TrySetException(exception);
434434
}
435435
}
436436
// SEND_COMPLETE expected, buffer and lock will be released then.
@@ -487,15 +487,15 @@ public void Abort(QuicAbortDirection abortDirection, long errorCode)
487487

488488
if (abortDirection.HasFlag(QuicAbortDirection.Read))
489489
{
490-
_receiveTcs.TrySetException(ThrowHelper.GetOperationAbortedException(SR.net_quic_reading_aborted), final: true);
490+
_receiveTcs.TrySetException(ThrowHelper.GetOperationAbortedException(SR.net_quic_reading_aborted));
491491
}
492492
if (abortDirection.HasFlag(QuicAbortDirection.Write))
493493
{
494494
var exception = ThrowHelper.GetOperationAbortedException(SR.net_quic_writing_aborted);
495495
Interlocked.CompareExchange(ref _sendException, exception, null);
496496
if (Interlocked.CompareExchange(ref _sendLocked, 1, 0) == 0)
497497
{
498-
_sendTcs.TrySetException(_sendException, final: true);
498+
_sendTcs.TrySetException(_sendException);
499499
Volatile.Write(ref _sendLocked, 0);
500500
}
501501
}
@@ -585,7 +585,7 @@ private unsafe int HandleEventSendComplete(ref SEND_COMPLETE_DATA data)
585585
Exception? exception = Volatile.Read(ref _sendException);
586586
if (exception is not null)
587587
{
588-
_sendTcs.TrySetException(exception, final: true);
588+
_sendTcs.TrySetException(exception);
589589
}
590590
if (data.Canceled == 0)
591591
{
@@ -604,12 +604,12 @@ private unsafe int HandleEventPeerSendShutdown()
604604
}
605605
private unsafe int HandleEventPeerSendAborted(ref PEER_SEND_ABORTED_DATA data)
606606
{
607-
_receiveTcs.TrySetException(ThrowHelper.GetStreamAbortedException((long)data.ErrorCode), final: true);
607+
_receiveTcs.TrySetException(ThrowHelper.GetStreamAbortedException((long)data.ErrorCode));
608608
return QUIC_STATUS_SUCCESS;
609609
}
610610
private unsafe int HandleEventPeerReceiveAborted(ref PEER_RECEIVE_ABORTED_DATA data)
611611
{
612-
_sendTcs.TrySetException(ThrowHelper.GetStreamAbortedException((long)data.ErrorCode), final: true);
612+
_sendTcs.TrySetException(ThrowHelper.GetStreamAbortedException((long)data.ErrorCode));
613613
return QUIC_STATUS_SUCCESS;
614614
}
615615
private unsafe int HandleEventSendShutdownComplete(ref SEND_SHUTDOWN_COMPLETE_DATA data)
@@ -639,8 +639,8 @@ private unsafe int HandleEventShutdownComplete(ref SHUTDOWN_COMPLETE_DATA data)
639639
(shutdownByApp: false, closedRemotely: false) => ThrowHelper.GetExceptionForMsQuicStatus(data.ConnectionCloseStatus, (long)data.ConnectionErrorCode),
640640
};
641641
_startedTcs.TrySetException(exception);
642-
_receiveTcs.TrySetException(exception, final: true);
643-
_sendTcs.TrySetException(exception, final: true);
642+
_receiveTcs.TrySetException(exception);
643+
_sendTcs.TrySetException(exception);
644644
}
645645
_startedTcs.TrySetException(ThrowHelper.GetOperationAbortedException());
646646
_shutdownTcs.TrySetResult();
@@ -766,11 +766,11 @@ unsafe void StreamShutdown(QUIC_STREAM_SHUTDOWN_FLAGS flags, long errorCode)
766766
{
767767
if (flags.HasFlag(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_RECEIVE) && !_receiveTcs.IsCompleted)
768768
{
769-
_receiveTcs.TrySetException(ThrowHelper.GetOperationAbortedException(SR.net_quic_reading_aborted), final: true);
769+
_receiveTcs.TrySetException(ThrowHelper.GetOperationAbortedException(SR.net_quic_reading_aborted));
770770
}
771771
if (flags.HasFlag(QUIC_STREAM_SHUTDOWN_FLAGS.ABORT_SEND) && !_sendTcs.IsCompleted)
772772
{
773-
_sendTcs.TrySetException(ThrowHelper.GetOperationAbortedException(SR.net_quic_writing_aborted), final: true);
773+
_sendTcs.TrySetException(ThrowHelper.GetOperationAbortedException(SR.net_quic_writing_aborted));
774774
}
775775
}
776776
}

0 commit comments

Comments
 (0)