From 0fec639fec1e6979fa5aa2b07f25c30e39ce11c4 Mon Sep 17 00:00:00 2001 From: Dominion Date: Wed, 14 Jun 2023 02:26:54 -0400 Subject: [PATCH] ValueTask WhenAll Extensions --- .../Extensions/ValueTaskExtensions.cs | 169 ++++++++++++++++++ .../Tgstation.Server.Common.csproj | 1 + 2 files changed, 170 insertions(+) create mode 100644 src/Tgstation.Server.Common/Extensions/ValueTaskExtensions.cs diff --git a/src/Tgstation.Server.Common/Extensions/ValueTaskExtensions.cs b/src/Tgstation.Server.Common/Extensions/ValueTaskExtensions.cs new file mode 100644 index 0000000000..884b30379a --- /dev/null +++ b/src/Tgstation.Server.Common/Extensions/ValueTaskExtensions.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Tgstation.Server.Common.Extensions +{ + /// + /// Extension methods for the and es. + /// + public static class ValueTaskExtensions + { + /// + /// Fully a given list of . + /// + /// The type. + /// An of s. + /// The number of elements in . + /// A representing the combined . + /// An containing any s thrown by the . + public static async ValueTask WhenAll(IEnumerable> tasks, int totalTasks) + { + if (tasks == null) + throw new ArgumentNullException(nameof(tasks)); + + // We don't allocate the list if no task throws + List? exceptions = null; + int i = 0; + var results = new T[totalTasks]; + foreach (var task in tasks) + { + try + { + results[i] = await task.ConfigureAwait(false); + } + catch (Exception ex) + { + exceptions ??= new (totalTasks - i); + exceptions.Add(ex); + } + + ++i; + } + + Debug.Assert(totalTasks == i, "Invalid count specified!"); + + if (exceptions != null) + throw new AggregateException(exceptions); + + return results; + } + + /// + /// Fully a given list of . + /// + /// The type. + /// An of s. + /// A containing the of results based on . + /// An containing any s thrown by the . + public static async ValueTask WhenAll(IReadOnlyList> tasks) + { + if (tasks == null) + throw new ArgumentNullException(nameof(tasks)); + + var totalTasks = tasks.Count; + if (totalTasks == 0) + return Array.Empty(); + + // We don't allocate the list if no task throws + List? exceptions = null; + var results = new T[totalTasks]; + for (var i = 0; i < totalTasks; i++) + try + { + results[i] = await tasks[i].ConfigureAwait(false); + } + catch (Exception ex) + { + exceptions ??= new (totalTasks - i); + exceptions.Add(ex); + } + + return exceptions == null + ? results + : throw new AggregateException(exceptions); + } + + /// + /// Fully a given list of . + /// + /// The type. + /// An of s. + /// A containing the of results based on . + /// An containing any s thrown by the . + public static ValueTask WhenAll(params ValueTask[] tasks) => WhenAll((IReadOnlyList>)tasks); + + /// + /// Fully a given list of . + /// + /// An of s. + /// The number of elements in . + /// A representing the combined . + /// An containing any s thrown by the . + public static async ValueTask WhenAll(IEnumerable tasks, int totalTasks) + { + if (tasks == null) + throw new ArgumentNullException(nameof(tasks)); + + // We don't allocate the list if no task throws + List? exceptions = null; + int i = 0; + foreach (var task in tasks) + { + try + { + await task.ConfigureAwait(false); + } + catch (Exception ex) + { + exceptions ??= new (totalTasks - i); + exceptions.Add(ex); + } + + ++i; + } + + Debug.Assert(totalTasks == i, "Invalid count specified!"); + + if (exceptions != null) + throw new AggregateException(exceptions); + } + + /// + /// Fully a given list of . + /// + /// An of s. + /// A representing the combined . + /// An containing any s thrown by the . + public static async ValueTask WhenAll(IReadOnlyList tasks) + { + if (tasks == null) + throw new ArgumentNullException(nameof(tasks)); + + // We don't allocate the list if no task throws + List? exceptions = null; + foreach (var task in tasks) + try + { + await task.ConfigureAwait(false); + } + catch (Exception ex) + { + exceptions ??= new (); + exceptions.Add(ex); + } + + if (exceptions != null) + throw new AggregateException(exceptions); + } + + /// + /// Fully a given list of . + /// + /// An of s. + /// A representing the combined . + /// An containing any s thrown by the . + public static ValueTask WhenAll(params ValueTask[] tasks) => WhenAll((IReadOnlyList)tasks); + } +} diff --git a/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj b/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj index d7f263b375..802abe894a 100644 --- a/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj +++ b/src/Tgstation.Server.Common/Tgstation.Server.Common.csproj @@ -23,6 +23,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive +