diff --git a/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs b/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs index 14bc2f56436fe5..0fc24781a1677c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AggregateException.cs @@ -21,15 +21,15 @@ namespace System [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] public class AggregateException : Exception { - private readonly ReadOnlyCollection m_innerExceptions; // Complete set of exceptions. Do not rename (binary serialization) + private readonly Exception[] _innerExceptions; // Complete set of exceptions. + private ReadOnlyCollection? _rocView; // separate from _innerExceptions to enable trimming if InnerExceptions isn't used /// /// Initializes a new instance of the class. /// public AggregateException() - : base(SR.AggregateException_ctor_DefaultMessage) + : this(SR.AggregateException_ctor_DefaultMessage) { - m_innerExceptions = new ReadOnlyCollection(Array.Empty()); } /// @@ -40,7 +40,7 @@ public AggregateException() public AggregateException(string? message) : base(message) { - m_innerExceptions = new ReadOnlyCollection(Array.Empty()); + _innerExceptions = Array.Empty(); } /// @@ -59,7 +59,7 @@ public AggregateException(string? message, Exception innerException) throw new ArgumentNullException(nameof(innerException)); } - m_innerExceptions = new ReadOnlyCollection(new Exception[] { innerException }); + _innerExceptions = new[] { innerException }; } /// @@ -101,9 +101,7 @@ public AggregateException(params Exception[] innerExceptions) : /// An element of is /// null. public AggregateException(string? message, IEnumerable innerExceptions) - // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along - // null typed correctly. Otherwise, create an IList from the enumerable and pass that along. - : this(message, innerExceptions as IList ?? (innerExceptions == null ? (List)null! : new List(innerExceptions))) + : this(message, innerExceptions == null ? null : new List(innerExceptions).ToArray(), cloneExceptions: false) { } @@ -118,43 +116,29 @@ public AggregateException(string? message, IEnumerable innerException /// An element of is /// null. public AggregateException(string? message, params Exception[] innerExceptions) : - this(message, (IList)innerExceptions) + this(message, innerExceptions, cloneExceptions: true) { } - /// - /// Allocates a new aggregate exception with the specified message and list of inner exceptions. - /// - /// The error message that explains the reason for the exception. - /// The exceptions that are the cause of the current exception. - /// The argument - /// is null. - /// An element of is - /// null. - private AggregateException(string? message, IList innerExceptions) - : base(message, innerExceptions != null && innerExceptions.Count > 0 ? innerExceptions[0] : null) + private AggregateException(string? message, Exception[]? innerExceptions, bool cloneExceptions) : + base(message, innerExceptions?.Length > 0 ? innerExceptions[0] : null) { if (innerExceptions == null) { throw new ArgumentNullException(nameof(innerExceptions)); } - // Copy exceptions to our internal array and validate them. We must copy them, - // because we're going to put them into a ReadOnlyCollection which simply reuses - // the list passed in to it. We don't want callers subsequently mutating. - Exception[] exceptionsCopy = new Exception[innerExceptions.Count]; + _innerExceptions = cloneExceptions ? new Exception[innerExceptions.Length] : innerExceptions; - for (int i = 0; i < exceptionsCopy.Length; i++) + for (int i = 0; i < _innerExceptions.Length; i++) { - exceptionsCopy[i] = innerExceptions[i]; + _innerExceptions[i] = innerExceptions[i]; - if (exceptionsCopy[i] == null) + if (innerExceptions[i] == null) { throw new ArgumentException(SR.AggregateException_ctor_InnerExceptionNull); } } - - m_innerExceptions = new ReadOnlyCollection(exceptionsCopy); } /// @@ -168,7 +152,7 @@ private AggregateException(string? message, IList innerExceptions) /// is null. /// An element of is /// null. - internal AggregateException(IEnumerable innerExceptionInfos) : + internal AggregateException(List innerExceptionInfos) : this(SR.AggregateException_ctor_DefaultMessage, innerExceptionInfos) { } @@ -186,54 +170,16 @@ internal AggregateException(IEnumerable innerExceptionInf /// is null. /// An element of is /// null. - internal AggregateException(string message, IEnumerable innerExceptionInfos) - // If it's already an IList, pass that along (a defensive copy will be made in the delegated ctor). If it's null, just pass along - // null typed correctly. Otherwise, create an IList from the enumerable and pass that along. - : this(message, innerExceptionInfos as IList ?? - (innerExceptionInfos == null ? - (List)null! : - new List(innerExceptionInfos))) - { - } - - /// - /// Allocates a new aggregate exception with the specified message and list of inner - /// exception dispatch info objects. - /// - /// The error message that explains the reason for the exception. - /// - /// Information about the exceptions that are the cause of the current exception. - /// - /// The argument - /// is null. - /// An element of is - /// null. - private AggregateException(string message, IList innerExceptionInfos) - : base(message, innerExceptionInfos != null && innerExceptionInfos.Count > 0 && innerExceptionInfos[0] != null ? - innerExceptionInfos[0].SourceException : null) + internal AggregateException(string message, List innerExceptionInfos) + : base(message, innerExceptionInfos.Count != 0 ? innerExceptionInfos[0].SourceException : null) { - if (innerExceptionInfos == null) - { - throw new ArgumentNullException(nameof(innerExceptionInfos)); - } + _innerExceptions = new Exception[innerExceptionInfos.Count]; - // Copy exceptions to our internal array and validate them. We must copy them, - // because we're going to put them into a ReadOnlyCollection which simply reuses - // the list passed in to it. We don't want callers subsequently mutating. - Exception[] exceptionsCopy = new Exception[innerExceptionInfos.Count]; - - for (int i = 0; i < exceptionsCopy.Length; i++) + for (int i = 0; i < _innerExceptions.Length; i++) { - ExceptionDispatchInfo edi = innerExceptionInfos[i]; - if (edi != null) exceptionsCopy[i] = edi.SourceException; - - if (exceptionsCopy[i] == null) - { - throw new ArgumentException(SR.AggregateException_ctor_InnerExceptionNull); - } + _innerExceptions[i] = innerExceptionInfos[i].SourceException; + Debug.Assert(_innerExceptions[i] != null); } - - m_innerExceptions = new ReadOnlyCollection(exceptionsCopy); } /// @@ -248,18 +194,13 @@ private AggregateException(string message, IList innerExc protected AggregateException(SerializationInfo info, StreamingContext context) : base(info, context) { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } - - Exception[]? innerExceptions = info.GetValue("InnerExceptions", typeof(Exception[])) as Exception[]; + Exception[]? innerExceptions = info.GetValue("InnerExceptions", typeof(Exception[])) as Exception[]; // Do not rename (binary serialization) if (innerExceptions is null) { throw new SerializationException(SR.AggregateException_DeserializationFailure); } - m_innerExceptions = new ReadOnlyCollection(innerExceptions); + _innerExceptions = innerExceptions; } /// @@ -275,9 +216,7 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont { base.GetObjectData(info, context); - Exception[] innerExceptions = new Exception[m_innerExceptions.Count]; - m_innerExceptions.CopyTo(innerExceptions, 0); - info.AddValue("InnerExceptions", innerExceptions, typeof(Exception[])); + info.AddValue("InnerExceptions", _innerExceptions, typeof(Exception[])); // Do not rename (binary serialization) } /// @@ -302,7 +241,7 @@ public override Exception GetBaseException() /// Gets a read-only collection of the instances that caused the /// current exception. /// - public ReadOnlyCollection InnerExceptions => m_innerExceptions; + public ReadOnlyCollection InnerExceptions => _rocView ??= new ReadOnlyCollection(_innerExceptions); /// @@ -332,21 +271,21 @@ public void Handle(Func predicate) } List? unhandledExceptions = null; - for (int i = 0; i < m_innerExceptions.Count; i++) + for (int i = 0; i < _innerExceptions.Length; i++) { // If the exception was not handled, lazily allocate a list of unhandled // exceptions (to be rethrown later) and add it. - if (!predicate(m_innerExceptions[i])) + if (!predicate(_innerExceptions[i])) { unhandledExceptions ??= new List(); - unhandledExceptions.Add(m_innerExceptions[i]); + unhandledExceptions.Add(_innerExceptions[i]); } } // If there are unhandled exceptions remaining, throw them. if (unhandledExceptions != null) { - throw new AggregateException(Message, unhandledExceptions); + throw new AggregateException(Message, unhandledExceptions.ToArray(), cloneExceptions: false); } } @@ -400,7 +339,7 @@ public AggregateException Flatten() } } - return new AggregateException(GetType() == typeof(AggregateException) ? base.Message : Message, flattenedExceptions); + return new AggregateException(GetType() == typeof(AggregateException) ? base.Message : Message, flattenedExceptions.ToArray(), cloneExceptions: false); } /// Gets a message that describes the exception. @@ -408,7 +347,7 @@ public override string Message { get { - if (m_innerExceptions.Count == 0) + if (_innerExceptions.Length == 0) { return base.Message; } @@ -416,10 +355,10 @@ public override string Message StringBuilder sb = StringBuilderCache.Acquire(); sb.Append(base.Message); sb.Append(' '); - for (int i = 0; i < m_innerExceptions.Count; i++) + for (int i = 0; i < _innerExceptions.Length; i++) { sb.Append('('); - sb.Append(m_innerExceptions[i].Message); + sb.Append(_innerExceptions[i].Message); sb.Append(") "); } sb.Length--; @@ -436,14 +375,14 @@ public override string ToString() StringBuilder text = new StringBuilder(); text.Append(base.ToString()); - for (int i = 0; i < m_innerExceptions.Count; i++) + for (int i = 0; i < _innerExceptions.Length; i++) { - if (m_innerExceptions[i] == InnerException) + if (_innerExceptions[i] == InnerException) continue; // Already logged in base.ToString() text.Append(Environment.NewLineConst + InnerExceptionPrefix); text.AppendFormat(CultureInfo.InvariantCulture, SR.AggregateException_InnerException, i); - text.Append(m_innerExceptions[i].ToString()); + text.Append(_innerExceptions[i].ToString()); text.Append("<---"); text.AppendLine(); } @@ -460,6 +399,8 @@ public override string ToString() /// /// See https://docs.microsoft.com/en-us/visualstudio/debugger/using-the-debuggerdisplay-attribute /// - private int InnerExceptionCount => InnerExceptions.Count; + internal int InnerExceptionCount => _innerExceptions.Length; + + internal Exception[] InternalInnerExceptions => _innerExceptions; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs index e354b58dc7af0a..a8d80715ed3fee 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ConcurrentExclusiveSchedulerPair.cs @@ -226,14 +226,15 @@ private void CompleteTaskAsync() /// The faulted worker task that's initiating the shutdown. private void FaultWithTask(Task faultedTask) { - Debug.Assert(faultedTask != null && faultedTask.IsFaulted && faultedTask.Exception!.InnerExceptions.Count > 0, + Debug.Assert(faultedTask != null && faultedTask.IsFaulted && faultedTask.Exception!.InnerExceptionCount > 0, "Needs a task in the faulted state and thus with exceptions."); ContractAssertMonitorStatus(ValueLock, held: true); + AggregateException faultedException = faultedTask.Exception; // Store the faulted task's exceptions CompletionState cs = EnsureCompletionStateInitialized(); - cs.m_exceptions ??= new List(); - cs.m_exceptions.AddRange(faultedTask.Exception.InnerExceptions); + cs.m_exceptions ??= new List(faultedException.InnerExceptionCount); + cs.m_exceptions.AddRange(faultedException.InternalInnerExceptions); // Now that we're doomed, request completion RequestCompletion(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index 201e3168757842..ac29941ab069d3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -4816,8 +4816,8 @@ internal static void AddExceptionsForCompletedTask(ref List? exceptio // this will make sure it won't throw again in the implicit wait t.UpdateExceptionObservedStatus(); - exceptions ??= new List(ex.InnerExceptions.Count); - exceptions.AddRange(ex.InnerExceptions); + exceptions ??= new List(ex.InnerExceptionCount); + exceptions.AddRange(ex.InternalInnerExceptions); } }