diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index 76cdd034..05e94fea 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -1400,6 +1400,13 @@ void Task_Methods() new Task(func).WaitAsync(CancellationToken.None); new Task(func).WaitAsync(TimeSpan.Zero); new Task(func).WaitAsync(TimeSpan.Zero, CancellationToken.None); + Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.None); + Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); + Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); + Task.FromResult(0).ConfigureAwait(ConfigureAwaitOptions.None); + Task.FromResult(0).ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + Task.FromResult(0).ConfigureAwait(ConfigureAwaitOptions.ForceYielding); } #if FeatureMemory diff --git a/src/Polyfill/ConfigureAwaitOptions.cs b/src/Polyfill/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..5da948e3 --- /dev/null +++ b/src/Polyfill/ConfigureAwaitOptions.cs @@ -0,0 +1,44 @@ +#if !NET8_0_OR_GREATER + +namespace System.Threading.Tasks; + +/// +/// Options to control behavior when awaiting. +/// +//Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.configureawaitoptions?view=net-11.0 +[Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + + /// + /// Attempts to marshal the continuation back to the original + /// or present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + + /// + /// Avoids throwing an exception at the completion of awaiting a that ends + /// in the or state. + /// + SuppressThrowing = 2, + + /// + /// Forces an await on an already completed to behave as if the + /// wasn't yet completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} + +#else +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Threading.Tasks.ConfigureAwaitOptions))] +#endif diff --git a/src/Polyfill/Polyfill_Task.cs b/src/Polyfill/Polyfill_Task.cs index 546fabf6..3e26d500 100644 --- a/src/Polyfill/Polyfill_Task.cs +++ b/src/Polyfill/Polyfill_Task.cs @@ -107,3 +107,81 @@ public static async Task WaitAsync( } #endif +#if !NET8_0_OR_GREATER + + /// Configures an awaiter used to await this . + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.configureawait?view=net-11.0#system-threading-tasks-task-configureawait(system-threading-tasks-configureawaitoptions) + public static ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new ArgumentOutOfRangeException(nameof(options)); + } + + var task = target; + + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + + /// Configures an awaiter used to await this . + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1.configureawait?view=net-11.0#system-threading-tasks-task-1-configureawait(system-threading-tasks-configureawaitoptions) + public static ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new ArgumentOutOfRangeException(nameof(options)); + } + + var task = target; + + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + +#endif +} + diff --git a/src/Split/net461/ConfigureAwaitOptions.cs b/src/Split/net461/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net461/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net461/Polyfill_Task.cs b/src/Split/net461/Polyfill_Task.cs index 0658db9d..98b98c0e 100644 --- a/src/Split/net461/Polyfill_Task.cs +++ b/src/Split/net461/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/net462/ConfigureAwaitOptions.cs b/src/Split/net462/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net462/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net462/Polyfill_Task.cs b/src/Split/net462/Polyfill_Task.cs index 0658db9d..98b98c0e 100644 --- a/src/Split/net462/Polyfill_Task.cs +++ b/src/Split/net462/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/net47/ConfigureAwaitOptions.cs b/src/Split/net47/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net47/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net47/Polyfill_Task.cs b/src/Split/net47/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/net47/Polyfill_Task.cs +++ b/src/Split/net47/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/net471/ConfigureAwaitOptions.cs b/src/Split/net471/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net471/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net471/Polyfill_Task.cs b/src/Split/net471/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/net471/Polyfill_Task.cs +++ b/src/Split/net471/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/net472/ConfigureAwaitOptions.cs b/src/Split/net472/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net472/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net472/Polyfill_Task.cs b/src/Split/net472/Polyfill_Task.cs index 0658db9d..98b98c0e 100644 --- a/src/Split/net472/Polyfill_Task.cs +++ b/src/Split/net472/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/net48/ConfigureAwaitOptions.cs b/src/Split/net48/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net48/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net48/Polyfill_Task.cs b/src/Split/net48/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/net48/Polyfill_Task.cs +++ b/src/Split/net48/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/net481/ConfigureAwaitOptions.cs b/src/Split/net481/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net481/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net481/Polyfill_Task.cs b/src/Split/net481/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/net481/Polyfill_Task.cs +++ b/src/Split/net481/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/net5.0/ConfigureAwaitOptions.cs b/src/Split/net5.0/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net5.0/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net5.0/Polyfill_Task.cs b/src/Split/net5.0/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/net5.0/Polyfill_Task.cs +++ b/src/Split/net5.0/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/net6.0/ConfigureAwaitOptions.cs b/src/Split/net6.0/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net6.0/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net6.0/Polyfill_Task.cs b/src/Split/net6.0/Polyfill_Task.cs new file mode 100644 index 00000000..83d3ff76 --- /dev/null +++ b/src/Split/net6.0/Polyfill_Task.cs @@ -0,0 +1,70 @@ +// +#pragma warning disable +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +static partial class Polyfill +{ + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } +} diff --git a/src/Split/net7.0/ConfigureAwaitOptions.cs b/src/Split/net7.0/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/net7.0/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/net7.0/Polyfill_Task.cs b/src/Split/net7.0/Polyfill_Task.cs new file mode 100644 index 00000000..635dd45f --- /dev/null +++ b/src/Split/net7.0/Polyfill_Task.cs @@ -0,0 +1,70 @@ +// +#pragma warning disable +namespace Polyfills; + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +static partial class Polyfill +{ + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } +} diff --git a/src/Split/netcoreapp2.0/ConfigureAwaitOptions.cs b/src/Split/netcoreapp2.0/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/netcoreapp2.0/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/netcoreapp2.0/Polyfill_Task.cs b/src/Split/netcoreapp2.0/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/netcoreapp2.0/Polyfill_Task.cs +++ b/src/Split/netcoreapp2.0/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/netcoreapp2.1/ConfigureAwaitOptions.cs b/src/Split/netcoreapp2.1/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/netcoreapp2.1/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/netcoreapp2.1/Polyfill_Task.cs b/src/Split/netcoreapp2.1/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/netcoreapp2.1/Polyfill_Task.cs +++ b/src/Split/netcoreapp2.1/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/netcoreapp2.2/ConfigureAwaitOptions.cs b/src/Split/netcoreapp2.2/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/netcoreapp2.2/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/netcoreapp2.2/Polyfill_Task.cs b/src/Split/netcoreapp2.2/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/netcoreapp2.2/Polyfill_Task.cs +++ b/src/Split/netcoreapp2.2/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/netcoreapp3.0/ConfigureAwaitOptions.cs b/src/Split/netcoreapp3.0/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/netcoreapp3.0/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/netcoreapp3.0/Polyfill_Task.cs b/src/Split/netcoreapp3.0/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/netcoreapp3.0/Polyfill_Task.cs +++ b/src/Split/netcoreapp3.0/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/netcoreapp3.1/ConfigureAwaitOptions.cs b/src/Split/netcoreapp3.1/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/netcoreapp3.1/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/netcoreapp3.1/Polyfill_Task.cs b/src/Split/netcoreapp3.1/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/netcoreapp3.1/Polyfill_Task.cs +++ b/src/Split/netcoreapp3.1/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/netstandard2.0/ConfigureAwaitOptions.cs b/src/Split/netstandard2.0/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/netstandard2.0/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/netstandard2.0/Polyfill_Task.cs b/src/Split/netstandard2.0/Polyfill_Task.cs index 0658db9d..98b98c0e 100644 --- a/src/Split/netstandard2.0/Polyfill_Task.cs +++ b/src/Split/netstandard2.0/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/netstandard2.1/ConfigureAwaitOptions.cs b/src/Split/netstandard2.1/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/netstandard2.1/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/netstandard2.1/Polyfill_Task.cs b/src/Split/netstandard2.1/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/netstandard2.1/Polyfill_Task.cs +++ b/src/Split/netstandard2.1/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Split/uap10.0/ConfigureAwaitOptions.cs b/src/Split/uap10.0/ConfigureAwaitOptions.cs new file mode 100644 index 00000000..a0ad1127 --- /dev/null +++ b/src/Split/uap10.0/ConfigureAwaitOptions.cs @@ -0,0 +1,35 @@ +// +#pragma warning disable +namespace System.Threading.Tasks; +/// +/// Options to control behavior when awaiting. +/// +[System.Flags] +#if PolyUseEmbeddedAttribute +[global::Microsoft.CodeAnalysis.EmbeddedAttribute] +#endif +#if PolyPublic +public +#endif +enum ConfigureAwaitOptions +{ + /// + /// No options specified. + /// + None = 0, + /// + /// Attempts to marshal the continuation back to the original or + /// present on the originating thread at the time of the await. + /// + ContinueOnCapturedContext = 1, + /// + /// Avoids throwing an exception at the completion of awaiting a that ends in the + /// or state. + /// + SuppressThrowing = 2, + /// + /// Forces an await on an already completed to behave as if the wasn't yet + /// completed, such that the current asynchronous method will be forced to yield its execution. + /// + ForceYielding = 4 +} diff --git a/src/Split/uap10.0/Polyfill_Task.cs b/src/Split/uap10.0/Polyfill_Task.cs index 0658db9d..7a80f2c9 100644 --- a/src/Split/uap10.0/Polyfill_Task.cs +++ b/src/Split/uap10.0/Polyfill_Task.cs @@ -82,4 +82,63 @@ public static async Task WaitAsync( await ((Task) target).WaitAsync(timeout, cancellationToken); return target.Result; } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait(this Task target, ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.SuppressThrowing | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + await t; + } + } + if ((options & ConfigureAwaitOptions.SuppressThrowing) != 0) + { + task = SuppressThrowAsync(task); + static async Task SuppressThrowAsync(Task t) + { + try + { + await t; + } + catch + { + } + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } + /// Configures an awaiter used to await this . + public static System.Runtime.CompilerServices.ConfiguredTaskAwaitable ConfigureAwait( + this Task target, + ConfigureAwaitOptions options) + { + if ((options & ~(ConfigureAwaitOptions.ContinueOnCapturedContext | + ConfigureAwaitOptions.ForceYielding)) != 0) + { + throw new System.ArgumentOutOfRangeException(nameof(options)); + } + var task = target; + if ((options & ConfigureAwaitOptions.ForceYielding) != 0) + { + task = ForceYieldAsync(task); + static async Task ForceYieldAsync(Task t) + { + await Task.Yield(); + return await t; + } + } + return task.ConfigureAwait( + (options & ConfigureAwaitOptions.ContinueOnCapturedContext) != 0); + } } diff --git a/src/Tests/PolyfillTests_Task_ConfigureAwait.cs b/src/Tests/PolyfillTests_Task_ConfigureAwait.cs new file mode 100644 index 00000000..a0893664 --- /dev/null +++ b/src/Tests/PolyfillTests_Task_ConfigureAwait.cs @@ -0,0 +1,115 @@ +partial class PolyfillTests +{ + [Test] + public async Task Task_ConfigureAwait_None() => await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.None); + + [Test] + public async Task Task_ConfigureAwait_ContinueOnCapturedContext() => await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + + [Test] + public async Task Task_ConfigureAwait_ForceYielding() + { + // Task.CompletedTask is already completed + await Assert.That(Task.CompletedTask.IsCompleted).IsTrue(); + + // But with ForceYielding, the awaiter should not be synchronously completed + var awaiter = Task.CompletedTask + .ConfigureAwait(ConfigureAwaitOptions.ForceYielding) + .GetAwaiter(); + await Assert.That(awaiter.IsCompleted).IsFalse(); + + // It should still complete successfully when awaited + await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); + } + + [Test] + public async Task Task_ConfigureAwait_SuppressThrowing_Faulted() + { + var task = Task.FromException(new InvalidOperationException("test")); + await task.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); + } + + [Test] + public async Task Task_ConfigureAwait_SuppressThrowing_Canceled() + { + var cts = new CancelSource(); + cts.Cancel(); + var task = Task.FromCanceled(cts.Token); + await task.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); + } + + [Test] + public async Task Task_ConfigureAwait_SuppressThrowing_Completed() => await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); + + [Test] + public async Task Task_ConfigureAwait_ForceYielding_SuppressThrowing() + { + var task = Task.FromException(new InvalidOperationException("test")); + await task.ConfigureAwait( + ConfigureAwaitOptions.ForceYielding | ConfigureAwaitOptions.SuppressThrowing); + } + + [Test] + public async Task TaskT_ConfigureAwait_None() + { + var result = await Task.FromResult(42).ConfigureAwait(ConfigureAwaitOptions.None); + await Assert.That(result).IsEqualTo(42); + } + + [Test] + public async Task TaskT_ConfigureAwait_ContinueOnCapturedContext() + { + var result = await Task.FromResult(42) + .ConfigureAwait(ConfigureAwaitOptions.ContinueOnCapturedContext); + await Assert.That(result).IsEqualTo(42); + } + + [Test] + public async Task TaskT_ConfigureAwait_ForceYielding() + { + var completedTask = Task.FromResult(42); + + // Task is already completed + await Assert.That(completedTask.IsCompleted).IsTrue(); + + // But with ForceYielding, the awaiter should not be synchronously completed + var awaiter = completedTask + .ConfigureAwait(ConfigureAwaitOptions.ForceYielding) + .GetAwaiter(); + await Assert.That(awaiter.IsCompleted).IsFalse(); + + // It should still return the correct result when awaited + var result = await completedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); + await Assert.That(result).IsEqualTo(42); + } + + [Test] + public async Task TaskT_ConfigureAwait_SuppressThrowing_Throws() + { + var exception = await Assert.That(() => + Task.FromResult(42).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing).GetAwaiter().GetResult() + ).Throws(); + + await Assert.That(exception!.ParamName).IsEqualTo("options"); + } + + [Test] + public async Task Task_ConfigureAwait_InvalidFlags_Throws() + { + var exception = await Assert.That(() => + Task.CompletedTask.ConfigureAwait((ConfigureAwaitOptions)8) + ).Throws(); + + await Assert.That(exception!.ParamName).IsEqualTo("options"); + } + + [Test] + public async Task TaskT_ConfigureAwait_InvalidFlags_Throws() + { + var exception = await Assert.That(() => + Task.FromResult(42).ConfigureAwait((ConfigureAwaitOptions)8) + ).Throws(); + + await Assert.That(exception!.ParamName).IsEqualTo("options"); + } +}