Skip to content

Commit f4ed767

Browse files
authored
Fix assert / ObjectDisposedException in PLINQ handling of AggregateException (#47711)
PLINQ's QueryTaskGroupState handles joining with queued tasks, and handles the exceptions that emerge from waiting on them. AggregateExceptions are special-cased, and are flattened to eliminate extra levels that may have been added explicitly or implicitly; the code then checks to see whether all of the exceptions contained in the aggregate represent cancellation. However, it fails to account for the possibility that there are no exceptions it contains, which could happen if the only exceptions thrown are themselves empty AggregateExceptions. If that happens, the code makes some invalid assumptions that lead to a failed assert and then throwing an ObjectDisposedException. The fix is just to check whether the flattened AggregateException is empty and to treat that not as cancellation but as a normal exception.
1 parent c9d1fd6 commit f4ed767

File tree

2 files changed

+16
-1
lines changed

2 files changed

+16
-1
lines changed

src/libraries/System.Linq.Parallel/src/System/Linq/Parallel/Scheduling/QueryTaskGroupState.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ internal void QueryEnd(bool userInitiatedDispose)
127127

128128
// if all the exceptions were OCE(externalToken), then we will propagate only a single OCE(externalToken) below
129129
// otherwise, we flatten the aggregate (because the WaitAll above already aggregated) and rethrow.
130-
if (!allOCEsOnTrackedExternalCancellationToken)
130+
if (!allOCEsOnTrackedExternalCancellationToken || flattenedAE.InnerExceptions.Count == 0)
131131
throw flattenedAE; // Case #1
132132
}
133133
finally

src/libraries/System.Linq.Parallel/tests/QueryOperators/ForAllTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,20 @@ public static void ForAll_ArgumentNullException()
5757
AssertExtensions.Throws<ArgumentNullException>("source", () => ((ParallelQuery<int>)null).ForAll(x => { }));
5858
AssertExtensions.Throws<ArgumentNullException>("action", () => ParallelEnumerable.Range(0, 1).ForAll(null));
5959
}
60+
61+
[Fact]
62+
public static void ForAll_UserThrownAggregateException()
63+
{
64+
const int Count = 10;
65+
try
66+
{
67+
new int[Count].AsParallel().Select<int, int>(i => throw new AggregateException()).ForAll(_ => { });
68+
}
69+
catch (AggregateException e)
70+
{
71+
// PLINQ internals flatten all AggregateExceptions.
72+
Assert.Equal(0, e.InnerExceptions.Count);
73+
}
74+
}
6075
}
6176
}

0 commit comments

Comments
 (0)