Expose/test IAsyncDisposable.ConfigureAwait (dotnet/corefx#34783)
authorStephen Toub <stoub@microsoft.com>
Thu, 31 Jan 2019 15:21:30 +0000 (10:21 -0500)
committerGitHub <noreply@github.com>
Thu, 31 Jan 2019 15:21:30 +0000 (10:21 -0500)
Commit migrated from https://github.com/dotnet/corefx/commit/e6939de2997edf49444ac2c12ad22d5d5f6dea89

src/libraries/System.Threading.Tasks/ref/System.Threading.Tasks.cs
src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/ConfiguredAsyncDisposable.netcoreapp.cs [new file with mode: 0644]
src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/ConfiguredCancelableAsyncEnumerableTests.netcoreapp.cs
src/libraries/System.Threading.Tasks/tests/System.Threading.Tasks.Tests.csproj

index c0f458d..1607e6a 100644 (file)
@@ -87,6 +87,11 @@ namespace System.Runtime.CompilerServices
         public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { }
         public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine { }
     }
+    public readonly struct ConfiguredAsyncDisposable
+    {
+        private readonly object _dummy;
+        public System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable DisposeAsync() { throw null; }
+    }
     public readonly struct ConfiguredCancelableAsyncEnumerable<T>
     {
         private readonly object _dummy;
@@ -163,6 +168,7 @@ namespace System.Threading.Tasks
     }
     public static partial class TaskExtensions
     {
+        public static System.Runtime.CompilerServices.ConfiguredAsyncDisposable ConfigureAwait(this System.IAsyncDisposable source, bool continueOnCapturedContext) { throw null; }
         public static System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable<T> ConfigureAwait<T>(this System.Collections.Generic.IAsyncEnumerable<T> source, bool continueOnCapturedContext) { throw null; }
         public static System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable<T> WithCancellation<T>(this System.Collections.Generic.IAsyncEnumerable<T> source, CancellationToken cancellationToken) { throw null; }
         public static System.Threading.Tasks.Task Unwrap(this System.Threading.Tasks.Task<System.Threading.Tasks.Task> task) { throw null; }
diff --git a/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/ConfiguredAsyncDisposable.netcoreapp.cs b/src/libraries/System.Threading.Tasks/tests/System.Runtime.CompilerServices/ConfiguredAsyncDisposable.netcoreapp.cs
new file mode 100644 (file)
index 0000000..337f2f2
--- /dev/null
@@ -0,0 +1,72 @@
+// 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.Threading.Tasks;
+using System.Threading.Tasks.Sources;
+using Xunit;
+
+namespace System.Runtime.CompilerServices.Tests
+{
+    public class ConfiguredAsyncDisposableTests
+    {
+        [Fact]
+        public void Default_GetAsyncEnumerator_Throws()
+        {
+            ConfiguredAsyncDisposable d = default;
+            Assert.Throws<NullReferenceException>(() => d.DisposeAsync());
+        }
+
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public void DisposeAsync_InvokesUnderlyingDisposeAsync(bool continueOnCapturedContext)
+        {
+            int invokeCount = 0;
+            var tcs = new TaskCompletionSource<int>();
+            var vt = new ValueTask(tcs.Task);
+
+            var d = new CustomAsyncDisposable(() =>
+            {
+                invokeCount++;
+                return vt;
+            });
+
+            Assert.Equal(vt.ConfigureAwait(continueOnCapturedContext), d.ConfigureAwait(continueOnCapturedContext).DisposeAsync());
+            Assert.Equal(1, invokeCount);
+        }
+
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public void DisposeAsync_ContinuesOnCapturedContextIfExpected(bool continueOnCapturedContext)
+        {
+            var d = new TrackingAsyncDisposable();
+            d.ConfigureAwait(continueOnCapturedContext).DisposeAsync().GetAwaiter().UnsafeOnCompleted(() => { });
+            Assert.Equal(
+                continueOnCapturedContext ? ValueTaskSourceOnCompletedFlags.UseSchedulingContext : ValueTaskSourceOnCompletedFlags.None,
+                d.Flags);
+        }
+
+        private sealed class CustomAsyncDisposable : IAsyncDisposable
+        {
+            private readonly Func<ValueTask> _action;
+
+            public CustomAsyncDisposable(Func<ValueTask> action) => _action = action;
+
+            public ValueTask DisposeAsync() => _action();
+        }
+
+        private sealed class TrackingAsyncDisposable : IAsyncDisposable, IValueTaskSource
+        {
+            public ValueTaskSourceOnCompletedFlags Flags;
+
+            public ValueTask DisposeAsync() => new ValueTask(this, 0);
+
+            public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => Flags = flags;
+            public ValueTaskSourceStatus GetStatus(short token) => ValueTaskSourceStatus.Pending;
+            public bool GetResult(short token) => throw new NotImplementedException();
+            void IValueTaskSource.GetResult(short token) => throw new NotImplementedException();
+        }
+    }
+}
index 1345543..bc2661c 100644 (file)
@@ -159,7 +159,7 @@ namespace System.Runtime.CompilerServices.Tests
             Assert.Equal(token1, enumerable.CancellationToken);
         }
 
-        private sealed class TrackFlagsAsyncEnumerable : IAsyncEnumerable<int>, IAsyncEnumerator<int>, IValueTaskSource<bool>, IValueTaskSource
+        private sealed class TrackFlagsAsyncEnumerable : IAsyncEnumerable<int>
         {
             public ValueTaskSourceOnCompletedFlags Flags;
             public CancellationToken CancellationToken;
@@ -167,17 +167,24 @@ namespace System.Runtime.CompilerServices.Tests
             public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default)
             {
                 CancellationToken = cancellationToken;
-                return this;
+                return new Enumerator(this);
             }
 
-            public ValueTask<bool> MoveNextAsync() => new ValueTask<bool>(this, 0);
-            public int Current => throw new NotImplementedException();
-            public ValueTask DisposeAsync() => new ValueTask(this, 0);
+            private sealed class Enumerator : IAsyncEnumerator<int>, IValueTaskSource<bool>, IValueTaskSource
+            {
+                private readonly TrackFlagsAsyncEnumerable _enumerable;
+
+                public Enumerator(TrackFlagsAsyncEnumerable enumerable) => _enumerable = enumerable;
 
-            public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => Flags = flags;
-            public ValueTaskSourceStatus GetStatus(short token) => ValueTaskSourceStatus.Pending;
-            public bool GetResult(short token) => throw new NotImplementedException();
-            void IValueTaskSource.GetResult(short token) => throw new NotImplementedException();
+                public ValueTask<bool> MoveNextAsync() => new ValueTask<bool>(this, 0);
+                public int Current => throw new NotImplementedException();
+                public ValueTask DisposeAsync() => new ValueTask(this, 0);
+
+                public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _enumerable.Flags = flags;
+                public ValueTaskSourceStatus GetStatus(short token) => ValueTaskSourceStatus.Pending;
+                public bool GetResult(short token) => throw new NotImplementedException();
+                void IValueTaskSource.GetResult(short token) => throw new NotImplementedException();
+            }
         }
 
         private sealed class EnumerableWithDelayToAsyncEnumerable<T> : IAsyncEnumerable<T>, IAsyncEnumerator<T>
index e43398d..9346139 100644 (file)
@@ -58,6 +58,7 @@
     <Compile Include="Task\TaskCanceledExceptionTests.netcoreapp.cs" />
     <Compile Include="Task\TaskStatusTest.netcoreapp.cs" />
     <Compile Include="System.Runtime.CompilerServices\AsyncTaskMethodBuilderTests.netcoreapp.cs" />
+    <Compile Include="System.Runtime.CompilerServices\ConfiguredAsyncDisposable.netcoreapp.cs" />
     <Compile Include="System.Runtime.CompilerServices\ConfiguredCancelableAsyncEnumerableTests.netcoreapp.cs" />
     <Compile Include="$(CommonTestPath)\System\Diagnostics\Tracing\TestEventListener.cs">
       <Link>Common\System\Diagnostics\Tracing\TestEventListener.cs</Link>