Selaa lähdekoodia

Add support for configured disposing

Oren Novotny 6 vuotta sitten
vanhempi
sitoutus
45e812740a

+ 3 - 3
Ix.NET/Source/System.Interactive.Async/System/Linq/Operators/Amb.cs

@@ -95,7 +95,7 @@ namespace System.Linq
 
                 try
                 {
-                    await using (winner.ConfigureAwait(false))
+                    await using (AsyncEnumerableExtensions.ConfigureAwait(winner, false))
                     {
                         if (!await moveNextWinner.ConfigureAwait(false))
                         {
@@ -204,7 +204,7 @@ namespace System.Linq
 
                 try
                 {
-                    await using (winner.ConfigureAwait(false))
+                    await using (AsyncEnumerableExtensions.ConfigureAwait(winner, false))
                     {
                         if (!await moveNextWinner.ConfigureAwait(false))
                         {
@@ -246,7 +246,7 @@ namespace System.Linq
         {
             if (enumerator != null)
             {
-                await using (enumerator.ConfigureAwait(false))
+                await using (AsyncEnumerableExtensions.ConfigureAwait(enumerator, false))
                 {
                     if (moveNextAsync != null)
                     {

+ 33 - 0
Ix.NET/Source/System.Linq.Async/System/Runtime/CompilerServices/ConfiguredAsyncDisposable.cs

@@ -0,0 +1,33 @@
+#if !BCL_HAS_CONFIGUREAWAIT
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+
+namespace System.Runtime.CompilerServices
+{
+    /// <summary>Provides a type that can be used to configure how awaits on an <see cref="IAsyncDisposable"/> are performed.</summary>
+    [StructLayout(LayoutKind.Auto)]
+    public readonly struct ConfiguredAsyncDisposable
+    {
+        private readonly IAsyncDisposable _source;
+        private readonly bool _continueOnCapturedContext;
+
+        internal ConfiguredAsyncDisposable(IAsyncDisposable source, bool continueOnCapturedContext)
+        {
+            _source = source;
+            _continueOnCapturedContext = continueOnCapturedContext;
+        }
+
+        public ConfiguredValueTaskAwaitable DisposeAsync() =>
+            // as with other "configured" awaitable-related type in CompilerServices, we don't null check to defend against
+            // misuse like `default(ConfiguredAsyncDisposable).DisposeAsync()`, which will null ref by design.
+            _source.DisposeAsync().ConfigureAwait(_continueOnCapturedContext);
+    }
+}
+#else
+using System.Runtime.CompilerServices;
+
+[assembly: TypeForwardedTo(typeof(ConfiguredAsyncDisposable))]
+#endif

+ 26 - 0
Ix.NET/Source/System.Linq.Async/System/Threading/Tasks/AsyncEnumerableExtensions.cs

@@ -11,6 +11,13 @@ namespace System.Threading.Tasks
     {
 #if !BCL_HAS_CONFIGUREAWAIT // https://github.com/dotnet/coreclr/pull/21939
 
+        /// <summary>Configures how awaits on the tasks returned from an async disposable will be performed.</summary>
+        /// <param name="source">The source async disposable.</param>
+        /// <param name="continueOnCapturedContext">Whether to capture and marshal back to the current context.</param>
+        /// <returns>The configured async disposable.</returns>
+        public static ConfiguredAsyncDisposable ConfigureAwait(this IAsyncDisposable source, bool continueOnCapturedContext) =>
+            new ConfiguredAsyncDisposable(source, continueOnCapturedContext);
+
         /// <summary>Configures how awaits on the tasks returned from an async iteration will be performed.</summary>
         /// <typeparam name="T">The type of the objects being iterated.</typeparam>
         /// <param name="source">The source enumerable being iterated.</param>
@@ -28,9 +35,19 @@ namespace System.Threading.Tasks
         public static ConfiguredCancelableAsyncEnumerable<T> WithCancellation<T>(
             this IAsyncEnumerable<T> source, CancellationToken cancellationToken) =>
             new ConfiguredCancelableAsyncEnumerable<T>(source, continueOnCapturedContext: true, cancellationToken);
+
+
 #elif !REFERENCE_ASSEMBLY
         // we need to carry an impl that delegates to the BCL version of these
 
+        /// <summary>Configures how awaits on the tasks returned from an async disposable will be performed.</summary>
+        /// <param name="source">The source async disposable.</param>
+        /// <param name="continueOnCapturedContext">Whether to capture and marshal back to the current context.</param>
+        /// <returns>The configured async disposable.</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static ConfiguredAsyncDisposable ConfigureAwait(this IAsyncDisposable source, bool continueOnCapturedContext) =>
+            TaskExtensions.ConfigureAwait(source, continueOnCapturedContext);
+
         /// <summary>Configures how awaits on the tasks returned from an async iteration will be performed.</summary>
         /// <typeparam name="T">The type of the objects being iterated.</typeparam>
         /// <param name="source">The source enumerable being iterated.</param>
@@ -51,6 +68,15 @@ namespace System.Threading.Tasks
 
 #else
         // Reference assembly, these won't be emmited, but keep these internal so we can compile
+
+        /// <summary>Configures how awaits on the tasks returned from an async disposable will be performed.</summary>
+        /// <param name="source">The source async disposable.</param>
+        /// <param name="continueOnCapturedContext">Whether to capture and marshal back to the current context.</param>
+        /// <returns>The configured async disposable.</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static ConfiguredAsyncDisposable ConfigureAwait(this IAsyncDisposable source, bool continueOnCapturedContext) =>
+            TaskExtensions.ConfigureAwait(source, continueOnCapturedContext);
+
         /// <summary>Configures how awaits on the tasks returned from an async iteration will be performed.</summary>
         /// <typeparam name="T">The type of the objects being iterated.</typeparam>
         /// <param name="source">The source enumerable being iterated.</param>