From d5989a77985bb238e50d59ded9d4aea755c254ad Mon Sep 17 00:00:00 2001 From: Matthias Vill Date: Sun, 15 Mar 2026 19:38:38 +0100 Subject: [PATCH] Compact StackStrace (.NET 6+) --- .../MorseCode.ITask.sln.DotSettings | 9 +- .../TaskInterfaceAsyncMethodBuilderTests.cs | 171 +++++++++++++++- Source/MorseCode.ITask/Tests/Tests.csproj | 18 +- .../_Root/AsyncMethodBuilderAttribute.cs | 28 +-- .../_Root/AwaiterInterfaceWrapper.cs | 3 + .../_Root/AwaiterInterfaceWrapper{TResult}.cs | 3 + .../_Root/ConfiguredTaskAwaiterWrapper.cs | 3 + .../ConfiguredTaskAwaiterWrapper{TResult}.cs | 3 + .../_Root/TaskAwaiterWrapper.cs | 3 + .../_Root/TaskAwaiterWrapper{TResult}.cs | 3 + .../_Root/TaskInterfaceAsyncMethodBuilder.cs | 184 +++++++++--------- ...askInterfaceAsyncMethodBuilder{TResult}.cs | 184 +++++++++--------- Source/MorseCode.ITask/_Root/_Root.csproj | 2 +- 13 files changed, 406 insertions(+), 208 deletions(-) diff --git a/Source/MorseCode.ITask/MorseCode.ITask.sln.DotSettings b/Source/MorseCode.ITask/MorseCode.ITask.sln.DotSettings index 103a2c2..85b69cc 100644 --- a/Source/MorseCode.ITask/MorseCode.ITask.sln.DotSettings +++ b/Source/MorseCode.ITask/MorseCode.ITask.sln.DotSettings @@ -1,12 +1,12 @@  -------------------------------------------------------------------------------------------------------------------- -<copyright file="$FILENAME$" company="MorseCode Software"> -Copyright (c) $CURRENT_YEAR$ MorseCode Software +<copyright file="${File.FileName}" company="MorseCode Software"> +Copyright (c) ${CurrentDate.Year} MorseCode Software </copyright> <summary> The MIT License (MIT) -Copyright (c) $CURRENT_YEAR$ MorseCode Software +Copyright (c) ${CurrentDate.Year} MorseCode Software Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,4 +27,5 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE&#x SOFTWARE. </summary> -------------------------------------------------------------------------------------------------------------------- - 6 \ No newline at end of file + 6 + True \ No newline at end of file diff --git a/Source/MorseCode.ITask/Tests/TaskInterfaceAsyncMethodBuilderTests.cs b/Source/MorseCode.ITask/Tests/TaskInterfaceAsyncMethodBuilderTests.cs index c5f8666..7694676 100644 --- a/Source/MorseCode.ITask/Tests/TaskInterfaceAsyncMethodBuilderTests.cs +++ b/Source/MorseCode.ITask/Tests/TaskInterfaceAsyncMethodBuilderTests.cs @@ -1,12 +1,17 @@ -using NUnit.Framework; +using System; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; +using NUnit.Framework; +using NUnit.Framework.Legacy; namespace MorseCode.ITask.Tests { [TestFixture] public class TaskInterfaceAsyncMethodBuilderTests { + private const string StackTraceRedactionPattern = "\\w__[\\w_]+(?=[()<>.])|(?<=\\(\\)) in [^\r\n]+"; + [Test] public async Task TaskInterfaceAsyncMethodBuilderTask() { @@ -18,6 +23,88 @@ async ITask TaskInterfaceAsyncMethodBuilderTaskMethodAsync() await Task.Delay(50).ConfigureAwait(false); } + [Test] + [SetUICulture("")] + public void TaskInterfaceAsyncMethodBuilderTaskWithException() + { + var exception = Assert.ThrowsAsync(async () => + await TaskInterfaceAsyncMethodBuilderTaskWithExceptionMethodAsync().ConfigureAwait(false)); + Assert.AreEqual("Test-Case Exception", exception.Message); + var redactedStackTrace = Regex.Replace(exception.StackTrace, StackTraceRedactionPattern, ""); +#if NET6_0_OR_GREATER + Assert.AreEqual( + """ + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.TaskInterfaceAsyncMethodBuilderTaskWithExceptionMethodAsync() + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.() + at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.BlockUntilCompleted() + at NUnit.Framework.Internal.MessagePumpStrategy.NoMessagePumpStrategy.WaitForCompletion(AwaitAdapter awaiter) + at NUnit.Framework.Internal.AsyncToSyncAdapter.Await[TResult](TestExecutionContext context, Func`1 invoke) + at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(TestExecutionContext context, Func`1 invoke) + at NUnit.Framework.Assert.ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code, String message, Object[] args) + """, + redactedStackTrace); +#else + StringAssert.StartsWith( + """ + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests..MoveNext() + --- End of stack trace from previous location where exception was thrown --- + at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) + at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult() + at MorseCode.ITask.ConfiguredTaskAwaiterWrapper.MorseCode.ITask.IAwaiter.GetResult() + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.<>d.MoveNext() + --- End of stack trace from previous location where exception was thrown --- + at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) + """, + redactedStackTrace); +#endif + } + + [Test] + [SetUICulture("")] + public void TaskInterfaceAsyncMethodBuilderTaskWithExceptionNoConfigure() + { + var exception = Assert.ThrowsAsync(async () => + await TaskInterfaceAsyncMethodBuilderTaskWithExceptionMethodAsync()); + Assert.AreEqual("Test-Case Exception", exception.Message); + var redactedStackTrace = Regex.Replace(exception.StackTrace, StackTraceRedactionPattern, ""); +#if NET6_0_OR_GREATER + Assert.AreEqual( + """ + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.TaskInterfaceAsyncMethodBuilderTaskWithExceptionMethodAsync() + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.() + at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.BlockUntilCompleted() + at NUnit.Framework.Internal.MessagePumpStrategy.NoMessagePumpStrategy.WaitForCompletion(AwaitAdapter awaiter) + at NUnit.Framework.Internal.AsyncToSyncAdapter.Await[TResult](TestExecutionContext context, Func`1 invoke) + at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(TestExecutionContext context, Func`1 invoke) + at NUnit.Framework.Assert.ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code, String message, Object[] args) + """, + redactedStackTrace); +#else + StringAssert.StartsWith( + """ + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests..MoveNext() + --- End of stack trace from previous location where exception was thrown --- + at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) + at System.Runtime.CompilerServices.TaskAwaiter.GetResult() + at MorseCode.ITask.TaskAwaiterWrapper.MorseCode.ITask.IAwaiter.GetResult() + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.<>d.MoveNext() + --- End of stack trace from previous location where exception was thrown --- + at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) + """, + redactedStackTrace); +#endif + } + + async ITask TaskInterfaceAsyncMethodBuilderTaskWithExceptionMethodAsync() + { + await Task.Delay(50).ConfigureAwait(false); + throw new Exception("Test-Case Exception"); + } + [Test] public async Task TaskInterfaceAsyncMethodBuilderResultTask() { @@ -35,5 +122,87 @@ async ITask TaskInterfaceAsyncMethodBuilderResultTaskMethodAsync() })); return results.Length; } + + [Test] + [SetUICulture("")] + public void TaskInterfaceAsyncMethodBuilderResultTaskWithException() + { + var exception = Assert.ThrowsAsync(async () => + await TaskInterfaceAsyncMethodBuilderResultTaskWithExceptionMethodAsync().ConfigureAwait(false)); + Assert.AreEqual("Test-Case Exception", exception.Message); + var redactedStackTrace = Regex.Replace(exception.StackTrace, StackTraceRedactionPattern, ""); +#if NET6_0_OR_GREATER + Assert.AreEqual( + """ + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.TaskInterfaceAsyncMethodBuilderResultTaskWithExceptionMethodAsync() + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.() + at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.BlockUntilCompleted() + at NUnit.Framework.Internal.MessagePumpStrategy.NoMessagePumpStrategy.WaitForCompletion(AwaitAdapter awaiter) + at NUnit.Framework.Internal.AsyncToSyncAdapter.Await[TResult](TestExecutionContext context, Func`1 invoke) + at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(TestExecutionContext context, Func`1 invoke) + at NUnit.Framework.Assert.ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code, String message, Object[] args) + """, + redactedStackTrace); +#else + StringAssert.StartsWith( + """ + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests..MoveNext() + --- End of stack trace from previous location where exception was thrown --- + at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) + at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() + at MorseCode.ITask.ConfiguredTaskAwaiterWrapper`1.MorseCode.ITask.IAwaiter.GetResult() + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.<>d.MoveNext() + --- End of stack trace from previous location where exception was thrown --- + at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) + """, + redactedStackTrace); +#endif + } + + [Test] + [SetUICulture("")] + public void TaskInterfaceAsyncMethodBuilderResultTaskWithExceptionNoConfigure() + { + var exception = Assert.ThrowsAsync(async () => + await TaskInterfaceAsyncMethodBuilderResultTaskWithExceptionMethodAsync()); + Assert.AreEqual("Test-Case Exception", exception.Message); + var redactedStackTrace = Regex.Replace(exception.StackTrace, StackTraceRedactionPattern, ""); +#if NET6_0_OR_GREATER + Assert.AreEqual( + """ + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.TaskInterfaceAsyncMethodBuilderResultTaskWithExceptionMethodAsync() + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.() + at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.BlockUntilCompleted() + at NUnit.Framework.Internal.MessagePumpStrategy.NoMessagePumpStrategy.WaitForCompletion(AwaitAdapter awaiter) + at NUnit.Framework.Internal.AsyncToSyncAdapter.Await[TResult](TestExecutionContext context, Func`1 invoke) + at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(TestExecutionContext context, Func`1 invoke) + at NUnit.Framework.Assert.ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code, String message, Object[] args) + """, + redactedStackTrace); +#else + StringAssert.StartsWith( + """ + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests..MoveNext() + --- End of stack trace from previous location where exception was thrown --- + at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) + at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() + at MorseCode.ITask.TaskAwaiterWrapper`1.MorseCode.ITask.IAwaiter.GetResult() + at MorseCode.ITask.Tests.TaskInterfaceAsyncMethodBuilderTests.<>d.MoveNext() + --- End of stack trace from previous location where exception was thrown --- + at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() + at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) + """, + redactedStackTrace); +#endif + } + + async ITask TaskInterfaceAsyncMethodBuilderResultTaskWithExceptionMethodAsync() + { + await Task.Delay(50).ConfigureAwait(false); + throw new Exception("Test-Case Exception"); + } } } diff --git a/Source/MorseCode.ITask/Tests/Tests.csproj b/Source/MorseCode.ITask/Tests/Tests.csproj index 473787f..14b6b64 100644 --- a/Source/MorseCode.ITask/Tests/Tests.csproj +++ b/Source/MorseCode.ITask/Tests/Tests.csproj @@ -1,9 +1,10 @@  - netcoreapp1.0;netcoreapp2.0;net45 + net8.0;net10.0;net462 MorseCode.ITask.Tests MorseCode.ITask.Tests + 14 @@ -12,12 +13,15 @@ - - - - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/Source/MorseCode.ITask/_Root/AsyncMethodBuilderAttribute.cs b/Source/MorseCode.ITask/_Root/AsyncMethodBuilderAttribute.cs index e735a6e..e3f34b6 100644 --- a/Source/MorseCode.ITask/_Root/AsyncMethodBuilderAttribute.cs +++ b/Source/MorseCode.ITask/_Root/AsyncMethodBuilderAttribute.cs @@ -1,13 +1,15 @@ -namespace System.Runtime.CompilerServices -{ - sealed class AsyncMethodBuilderAttribute : Attribute - { - public Type BuilderType { get; } - - public AsyncMethodBuilderAttribute( - Type builderType) - { - BuilderType = builderType; - } - } -} +#if !NETSTANDARD2_1_OR_GREATER && !NETCOREAPP1_1_OR_GREATER +namespace System.Runtime.CompilerServices +{ + sealed class AsyncMethodBuilderAttribute : Attribute + { + public Type BuilderType { get; } + + public AsyncMethodBuilderAttribute( + Type builderType) + { + BuilderType = builderType; + } + } +} +#endif diff --git a/Source/MorseCode.ITask/_Root/AwaiterInterfaceWrapper.cs b/Source/MorseCode.ITask/_Root/AwaiterInterfaceWrapper.cs index 1c690c2..2aca00e 100644 --- a/Source/MorseCode.ITask/_Root/AwaiterInterfaceWrapper.cs +++ b/Source/MorseCode.ITask/_Root/AwaiterInterfaceWrapper.cs @@ -89,6 +89,9 @@ public void UnsafeOnCompleted(Action continuation) /// The awaiter was not properly initialized. /// The task was canceled. /// The task completed in a Faulted state. +#if NET6_0_OR_GREATER + [System.Diagnostics.StackTraceHidden] +#endif public void GetResult() { this.awaiter.GetResult(); diff --git a/Source/MorseCode.ITask/_Root/AwaiterInterfaceWrapper{TResult}.cs b/Source/MorseCode.ITask/_Root/AwaiterInterfaceWrapper{TResult}.cs index 29a0257..8975359 100644 --- a/Source/MorseCode.ITask/_Root/AwaiterInterfaceWrapper{TResult}.cs +++ b/Source/MorseCode.ITask/_Root/AwaiterInterfaceWrapper{TResult}.cs @@ -93,6 +93,9 @@ public void UnsafeOnCompleted(Action continuation) /// The awaiter was not properly initialized. /// The task was canceled. /// The task completed in a Faulted state. +#if NET6_0_OR_GREATER + [System.Diagnostics.StackTraceHidden] +#endif public TResult GetResult() { return this.awaiter.GetResult(); diff --git a/Source/MorseCode.ITask/_Root/ConfiguredTaskAwaiterWrapper.cs b/Source/MorseCode.ITask/_Root/ConfiguredTaskAwaiterWrapper.cs index a678d04..e84267e 100644 --- a/Source/MorseCode.ITask/_Root/ConfiguredTaskAwaiterWrapper.cs +++ b/Source/MorseCode.ITask/_Root/ConfiguredTaskAwaiterWrapper.cs @@ -62,6 +62,9 @@ void ICriticalNotifyCompletion.UnsafeOnCompleted(Action continuation) this.taskAwaiter.UnsafeOnCompleted(continuation); } +#if NET6_0_OR_GREATER + [System.Diagnostics.StackTraceHidden] +#endif void IAwaiter.GetResult() { this.taskAwaiter.GetResult(); diff --git a/Source/MorseCode.ITask/_Root/ConfiguredTaskAwaiterWrapper{TResult}.cs b/Source/MorseCode.ITask/_Root/ConfiguredTaskAwaiterWrapper{TResult}.cs index 2c48451..56b7b03 100644 --- a/Source/MorseCode.ITask/_Root/ConfiguredTaskAwaiterWrapper{TResult}.cs +++ b/Source/MorseCode.ITask/_Root/ConfiguredTaskAwaiterWrapper{TResult}.cs @@ -62,6 +62,9 @@ void ICriticalNotifyCompletion.UnsafeOnCompleted(Action continuation) this.taskAwaiter.UnsafeOnCompleted(continuation); } +#if NET6_0_OR_GREATER + [System.Diagnostics.StackTraceHidden] +#endif TResult IAwaiter.GetResult() { return this.taskAwaiter.GetResult(); diff --git a/Source/MorseCode.ITask/_Root/TaskAwaiterWrapper.cs b/Source/MorseCode.ITask/_Root/TaskAwaiterWrapper.cs index 9a5d6a0..7322033 100644 --- a/Source/MorseCode.ITask/_Root/TaskAwaiterWrapper.cs +++ b/Source/MorseCode.ITask/_Root/TaskAwaiterWrapper.cs @@ -62,6 +62,9 @@ void ICriticalNotifyCompletion.UnsafeOnCompleted(Action continuation) this.taskAwaiter.UnsafeOnCompleted(continuation); } +#if NET6_0_OR_GREATER + [System.Diagnostics.StackTraceHidden] +#endif void IAwaiter.GetResult() { this.taskAwaiter.GetResult(); diff --git a/Source/MorseCode.ITask/_Root/TaskAwaiterWrapper{TResult}.cs b/Source/MorseCode.ITask/_Root/TaskAwaiterWrapper{TResult}.cs index 508bef2..d8441c0 100644 --- a/Source/MorseCode.ITask/_Root/TaskAwaiterWrapper{TResult}.cs +++ b/Source/MorseCode.ITask/_Root/TaskAwaiterWrapper{TResult}.cs @@ -62,6 +62,9 @@ void ICriticalNotifyCompletion.UnsafeOnCompleted(Action continuation) this.taskAwaiter.UnsafeOnCompleted(continuation); } +#if NET6_0_OR_GREATER + [System.Diagnostics.StackTraceHidden] +#endif TResult IAwaiter.GetResult() { return this.taskAwaiter.GetResult(); diff --git a/Source/MorseCode.ITask/_Root/TaskInterfaceAsyncMethodBuilder.cs b/Source/MorseCode.ITask/_Root/TaskInterfaceAsyncMethodBuilder.cs index abe7dc2..72c6e01 100644 --- a/Source/MorseCode.ITask/_Root/TaskInterfaceAsyncMethodBuilder.cs +++ b/Source/MorseCode.ITask/_Root/TaskInterfaceAsyncMethodBuilder.cs @@ -1,91 +1,93 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace MorseCode.ITask.CompilerServices -{ - /// - /// Runtime helper for async methods returning . - /// - [StructLayout(LayoutKind.Auto)] - public struct TaskInterfaceAsyncMethodBuilder - { - /// - /// Delegate everything to the official method builder - /// since we just use - /// in the end. - /// - /// - /// - /// This must not be marked readonly because it is mutable. If marked - /// readonly, first await silently freezes everything. - /// - /// - AsyncTaskMethodBuilder builder; - - /// - /// Used by the compiler to generate the return value for the async method. - /// - public ITask Task => builder.Task.AsITask(); - - TaskInterfaceAsyncMethodBuilder(AsyncTaskMethodBuilder builder) : this() - { - this.builder = builder; - } - - - - /// - /// Part of async method builder contract: create a builder. - /// - public static TaskInterfaceAsyncMethodBuilder Create() - => new TaskInterfaceAsyncMethodBuilder( - AsyncTaskMethodBuilder.Create()); - - /// - /// Part of async method builder contract: set exception. - /// - public void SetException(Exception ex) => builder.SetException(ex); - - /// - /// Part of async method builder contract: set RanToCompletion. - /// - public void SetResult() => builder.SetResult(); - - /// - /// Part of async method builder contract: set the state machine to use - /// if the method ends up running asynchronously. - /// - public void SetStateMachine(IAsyncStateMachine stateMachine) => builder.SetStateMachine(stateMachine); - - /// - /// Part of async method builder contract: initialize and start running - /// the state machine. - /// - public void Start(ref TStateMachine stateMachine) - where TStateMachine : IAsyncStateMachine - => builder.Start(ref stateMachine); - - /// - /// Part of async method builder contract: called when an awaited operation - /// is pending completion to schedule a continuation. - /// - public void AwaitOnCompleted( - ref TAwaiter awaiter, - ref TStateMachine stateMachine) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine - => builder.AwaitOnCompleted(ref awaiter, ref stateMachine); - - /// - /// Part of async method builder contract: called when an awaited operation - /// is pending completion to schedule a continuation. - /// - public void AwaitUnsafeOnCompleted( - ref TAwaiter awaiter, - ref TStateMachine stateMachine) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine - => builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); - } -} +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MorseCode.ITask.CompilerServices +{ + /// + /// Runtime helper for async methods returning . + /// + [StructLayout(LayoutKind.Auto)] + public struct TaskInterfaceAsyncMethodBuilder + { + /// + /// Delegate everything to the official method builder + /// since we just use + /// in the end. + /// + /// + /// + /// This must not be marked readonly because it is mutable. If marked + /// readonly, first await silently freezes everything. + /// + /// + private AsyncTaskMethodBuilder builder; + + /// + /// Used by the compiler to generate the return value for the async method. + /// + public ITask Task => builder.Task.AsITask(); + + private TaskInterfaceAsyncMethodBuilder(AsyncTaskMethodBuilder builder) : this() + { + this.builder = builder; + } + + /// + /// Part of async method builder contract: create a builder. + /// + public static TaskInterfaceAsyncMethodBuilder Create() + => new TaskInterfaceAsyncMethodBuilder( + AsyncTaskMethodBuilder.Create()); + + /// + /// Part of async method builder contract: set exception. + /// + public void SetException(Exception ex) => builder.SetException(ex); + + /// + /// Part of async method builder contract: set RanToCompletion. + /// + public void SetResult() => builder.SetResult(); + + /// + /// Part of async method builder contract: set the state machine to use + /// if the method ends up running asynchronously. + /// + public void SetStateMachine(IAsyncStateMachine stateMachine) => builder.SetStateMachine(stateMachine); + + /// + /// Part of async method builder contract: initialize and start running + /// the state machine. + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Start(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine + => builder.Start(ref stateMachine); + + /// + /// Part of async method builder contract: called when an awaited operation + /// is pending completion to schedule a continuation. + /// + public void AwaitOnCompleted( + ref TAwaiter awaiter, + ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + => builder.AwaitOnCompleted(ref awaiter, ref stateMachine); + + /// + /// Part of async method builder contract: called when an awaited operation + /// is pending completion to schedule a continuation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AwaitUnsafeOnCompleted( + ref TAwaiter awaiter, + ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + => builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); + } +} diff --git a/Source/MorseCode.ITask/_Root/TaskInterfaceAsyncMethodBuilder{TResult}.cs b/Source/MorseCode.ITask/_Root/TaskInterfaceAsyncMethodBuilder{TResult}.cs index 74e5c4e..3871000 100644 --- a/Source/MorseCode.ITask/_Root/TaskInterfaceAsyncMethodBuilder{TResult}.cs +++ b/Source/MorseCode.ITask/_Root/TaskInterfaceAsyncMethodBuilder{TResult}.cs @@ -1,91 +1,93 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace MorseCode.ITask.CompilerServices -{ - /// - /// Runtime helper for async methods returning . - /// - [StructLayout(LayoutKind.Auto)] - public struct TaskInterfaceAsyncMethodBuilder - { - /// - /// Delegate everything to the official method builder - /// since we just use - /// in the end. - /// - /// - /// - /// This must not be marked readonly because it is mutable. If marked - /// readonly, first await silently freezes everything. - /// - /// - AsyncTaskMethodBuilder builder; - - /// - /// Used by the compiler to generate the return value for the async method. - /// - public ITask Task => builder.Task.AsITask(); - - TaskInterfaceAsyncMethodBuilder(AsyncTaskMethodBuilder builder) : this() - { - this.builder = builder; - } - - - - /// - /// Part of async method builder contract: create a builder. - /// - public static TaskInterfaceAsyncMethodBuilder Create() - => new TaskInterfaceAsyncMethodBuilder( - AsyncTaskMethodBuilder.Create()); - - /// - /// Part of async method builder contract: set exception. - /// - public void SetException(Exception ex) => builder.SetException(ex); - - /// - /// Part of async method builder contract: set RanToCompletion. - /// - public void SetResult(TResult result) => builder.SetResult(result); - - /// - /// Part of async method builder contract: set the state machine to use - /// if the method ends up running asynchronously. - /// - public void SetStateMachine(IAsyncStateMachine stateMachine) => builder.SetStateMachine(stateMachine); - - /// - /// Part of async method builder contract: initialize and start running - /// the state machine. - /// - public void Start(ref TStateMachine stateMachine) - where TStateMachine : IAsyncStateMachine - => builder.Start(ref stateMachine); - - /// - /// Part of async method builder contract: called when an awaited operation - /// is pending completion to schedule a continuation. - /// - public void AwaitOnCompleted( - ref TAwaiter awaiter, - ref TStateMachine stateMachine) - where TAwaiter : INotifyCompletion - where TStateMachine : IAsyncStateMachine - => builder.AwaitOnCompleted(ref awaiter, ref stateMachine); - - /// - /// Part of async method builder contract: called when an awaited operation - /// is pending completion to schedule a continuation. - /// - public void AwaitUnsafeOnCompleted( - ref TAwaiter awaiter, - ref TStateMachine stateMachine) - where TAwaiter : ICriticalNotifyCompletion - where TStateMachine : IAsyncStateMachine - => builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); - } -} +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MorseCode.ITask.CompilerServices +{ + /// + /// Runtime helper for async methods returning . + /// + [StructLayout(LayoutKind.Auto)] + public struct TaskInterfaceAsyncMethodBuilder + { + /// + /// Delegate everything to the official method builder + /// since we just use + /// in the end. + /// + /// + /// + /// This must not be marked readonly because it is mutable. If marked + /// readonly, first await silently freezes everything. + /// + /// + private AsyncTaskMethodBuilder builder; + + /// + /// Used by the compiler to generate the return value for the async method. + /// + public ITask Task => builder.Task.AsITask(); + + private TaskInterfaceAsyncMethodBuilder(AsyncTaskMethodBuilder builder) : this() + { + this.builder = builder; + } + + /// + /// Part of async method builder contract: create a builder. + /// + public static TaskInterfaceAsyncMethodBuilder Create() + => new TaskInterfaceAsyncMethodBuilder( + AsyncTaskMethodBuilder.Create()); + + /// + /// Part of async method builder contract: set exception. + /// + public void SetException(Exception ex) => builder.SetException(ex); + + /// + /// Part of async method builder contract: set RanToCompletion. + /// + public void SetResult(TResult result) => builder.SetResult(result); + + /// + /// Part of async method builder contract: set the state machine to use + /// if the method ends up running asynchronously. + /// + public void SetStateMachine(IAsyncStateMachine stateMachine) => builder.SetStateMachine(stateMachine); + + /// + /// Part of async method builder contract: initialize and start running + /// the state machine. + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Start(ref TStateMachine stateMachine) + where TStateMachine : IAsyncStateMachine + => builder.Start(ref stateMachine); + + /// + /// Part of async method builder contract: called when an awaited operation + /// is pending completion to schedule a continuation. + /// + public void AwaitOnCompleted( + ref TAwaiter awaiter, + ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + => builder.AwaitOnCompleted(ref awaiter, ref stateMachine); + + /// + /// Part of async method builder contract: called when an awaited operation + /// is pending completion to schedule a continuation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AwaitUnsafeOnCompleted( + ref TAwaiter awaiter, + ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + => builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); + } +} diff --git a/Source/MorseCode.ITask/_Root/_Root.csproj b/Source/MorseCode.ITask/_Root/_Root.csproj index 409b6de..43a52fe 100644 --- a/Source/MorseCode.ITask/_Root/_Root.csproj +++ b/Source/MorseCode.ITask/_Root/_Root.csproj @@ -1,7 +1,7 @@  - netstandard1.0;netstandard2.0;net45 + netstandard1.0;netstandard2.0;netstandard2.1;net45;net6.0 2.0.0 MorseCode Software An awaitable covariant ITask interface which may be used in place of the built-in Task class.