ConfiguredCancelableAsyncEnumerable.cs 4.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. #if !BCL_HAS_CONFIGUREAWAIT // https://github.com/dotnet/coreclr/pull/21939
  2. // Licensed to the .NET Foundation under one or more agreements.
  3. // The .NET Foundation licenses this file to you under the MIT license.
  4. // See the LICENSE file in the project root for more information.
  5. using System.Collections.Generic;
  6. using System.Runtime.InteropServices;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace System.Runtime.CompilerServices
  10. {
  11. /// <summary>Provides an awaitable async enumerable that enables cancelable iteration and configured awaits.</summary>
  12. [StructLayout(LayoutKind.Auto)]
  13. public readonly struct ConfiguredCancelableAsyncEnumerable<T>
  14. {
  15. private readonly IAsyncEnumerable<T> _enumerable;
  16. private readonly CancellationToken _cancellationToken;
  17. private readonly bool _continueOnCapturedContext;
  18. internal ConfiguredCancelableAsyncEnumerable(IAsyncEnumerable<T> enumerable, bool continueOnCapturedContext, CancellationToken cancellationToken)
  19. {
  20. _enumerable = enumerable;
  21. _continueOnCapturedContext = continueOnCapturedContext;
  22. _cancellationToken = cancellationToken;
  23. }
  24. /// <summary>Configures how awaits on the tasks returned from an async iteration will be performed.</summary>
  25. /// <param name="continueOnCapturedContext">Whether to capture and marshal back to the current context.</param>
  26. /// <returns>The configured enumerable.</returns>
  27. /// <remarks>This will replace any previous value set by <see cref="ConfigureAwait(bool)"/> for this iteration.</remarks>
  28. public ConfiguredCancelableAsyncEnumerable<T> ConfigureAwait(bool continueOnCapturedContext) =>
  29. new ConfiguredCancelableAsyncEnumerable<T>(_enumerable, continueOnCapturedContext, _cancellationToken);
  30. /// <summary>Sets the <see cref="CancellationToken"/> to be passed to <see cref="IAsyncEnumerable{T}.GetAsyncEnumerator(CancellationToken)"/> when iterating.</summary>
  31. /// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
  32. /// <returns>The configured enumerable.</returns>
  33. /// <remarks>This will replace any previous <see cref="CancellationToken"/> set by <see cref="WithCancellation(CancellationToken)"/> for this iteration.</remarks>
  34. public ConfiguredCancelableAsyncEnumerable<T> WithCancellation(CancellationToken cancellationToken) =>
  35. new ConfiguredCancelableAsyncEnumerable<T>(_enumerable, _continueOnCapturedContext, cancellationToken);
  36. public Enumerator GetAsyncEnumerator() =>
  37. // as with other "configured" awaitable-related type in CompilerServices, we don't null check to defend against
  38. // misuse like `default(ConfiguredCancelableAsyncEnumerable<T>).GetAsyncEnumerator()`, which will null ref by design.
  39. new Enumerator(_enumerable.GetAsyncEnumerator(_cancellationToken), _continueOnCapturedContext);
  40. /// <summary>Provides an awaitable async enumerator that enables cancelable iteration and configured awaits.</summary>
  41. [StructLayout(LayoutKind.Auto)]
  42. public readonly struct Enumerator
  43. {
  44. private readonly IAsyncEnumerator<T> _enumerator;
  45. private readonly bool _continueOnCapturedContext;
  46. internal Enumerator(IAsyncEnumerator<T> enumerator, bool continueOnCapturedContext)
  47. {
  48. _enumerator = enumerator;
  49. _continueOnCapturedContext = continueOnCapturedContext;
  50. }
  51. /// <summary>Advances the enumerator asynchronously to the next element of the collection.</summary>
  52. /// <returns>
  53. /// A <see cref="ConfiguredValueTaskAwaitable{Boolean}"/> that will complete with a result of <c>true</c>
  54. /// if the enumerator was successfully advanced to the next element, or <c>false</c> if the enumerator has
  55. /// passed the end of the collection.
  56. /// </returns>
  57. public ConfiguredValueTaskAwaitable<bool> MoveNextAsync() =>
  58. _enumerator.MoveNextAsync().ConfigureAwait(_continueOnCapturedContext);
  59. /// <summary>Gets the element in the collection at the current position of the enumerator.</summary>
  60. public T Current => _enumerator.Current;
  61. /// <summary>
  62. /// Performs application-defined tasks associated with freeing, releasing, or
  63. /// resetting unmanaged resources asynchronously.
  64. /// </summary>
  65. public ConfiguredValueTaskAwaitable DisposeAsync() =>
  66. _enumerator.DisposeAsync().ConfigureAwait(_continueOnCapturedContext);
  67. }
  68. }
  69. }
  70. #else
  71. using System.Runtime.CompilerServices;
  72. [assembly: TypeForwardedTo(typeof(ConfiguredCancelableAsyncEnumerable<>))]
  73. #endif