|
| 1 | +#if !NET6_0_OR_GREATER |
| 2 | +namespace GraphQL.Server.Transports.AspNetCore; |
| 3 | + |
| 4 | +internal static class TaskExtensions |
| 5 | +{ |
| 6 | + /// <summary> |
| 7 | + /// Gets a <see cref="Task{TResult}"/> that will complete when this <see cref="Task{TResult}"/> completes, |
| 8 | + /// when the specified timeout expires, or when the specified <see cref="CancellationToken"/> has cancellation requested. |
| 9 | + /// </summary> |
| 10 | + /// <exception cref="OperationCanceledException"></exception> |
| 11 | + /// <exception cref="TimeoutException"></exception> |
| 12 | + public static Task<TResult> WaitAsync<TResult>(this Task<TResult> task, TimeSpan timeout, CancellationToken cancellationToken) |
| 13 | + { |
| 14 | + var millisecondsTimeout = (int)timeout.TotalMilliseconds; |
| 15 | + if (millisecondsTimeout < -1) |
| 16 | + throw new ArgumentOutOfRangeException(nameof(timeout)); |
| 17 | + if (task.IsCompleted || (millisecondsTimeout == -1 && !cancellationToken.CanBeCanceled)) |
| 18 | + return task; |
| 19 | + if (millisecondsTimeout == 0) |
| 20 | + return Task.FromException<TResult>(new TimeoutException()); |
| 21 | + if (cancellationToken.IsCancellationRequested) |
| 22 | + return Task.FromCanceled<TResult>(cancellationToken); |
| 23 | + |
| 24 | + return TimeoutAfter(task, millisecondsTimeout, cancellationToken); |
| 25 | + |
| 26 | + static async Task<TResult> TimeoutAfter(Task<TResult> task, int millisecondsDelay, CancellationToken cancellationToken) |
| 27 | + { |
| 28 | + // the CTS here ensures that the Task.Delay gets 'disposed' if the task finishes before the delay |
| 29 | + using var timeoutCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); |
| 30 | + var completedTask = await Task.WhenAny(task, Task.Delay(millisecondsDelay, timeoutCancellationTokenSource.Token)).ConfigureAwait(false); |
| 31 | + if (completedTask == task) |
| 32 | + { |
| 33 | + // discontinue the Task.Delay |
| 34 | + timeoutCancellationTokenSource.Cancel(); |
| 35 | + return await task.ConfigureAwait(false); // Very important in order to propagate exceptions |
| 36 | + } |
| 37 | + else |
| 38 | + { |
| 39 | + // was the cancellation token was signaled? |
| 40 | + cancellationToken.ThrowIfCancellationRequested(); |
| 41 | + // or did it timeout? |
| 42 | + throw new TimeoutException(); |
| 43 | + } |
| 44 | + } |
| 45 | + } |
| 46 | +} |
| 47 | +#endif |
0 commit comments