Skip to content

Commit 507866d

Browse files
committed
Fix race condition in TaskHelpersExtensions.Catch
1 parent 590ee02 commit 507866d

File tree

1 file changed

+13
-8
lines changed

1 file changed

+13
-8
lines changed

src/Common/TaskHelpersExtensions.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ internal static class TaskHelpersExtensions
2121
/// </summary>
2222
internal static Task Catch(this Task task, Func<CatchInfo, CatchInfo.CatchResult> continuation, CancellationToken cancellationToken = default(CancellationToken))
2323
{
24+
// Fast path for successful tasks, to prevent an extra TCS allocation
2425
if (task.Status == TaskStatus.RanToCompletion)
2526
{
2627
return task;
@@ -40,10 +41,12 @@ internal static class TaskHelpersExtensions
4041
/// </summary>
4142
internal static Task<TResult> Catch<TResult>(this Task<TResult> task, Func<CatchInfo<TResult>, CatchInfo<TResult>.CatchResult> continuation, CancellationToken cancellationToken = default(CancellationToken))
4243
{
44+
// Fast path for successful tasks, to prevent an extra TCS allocation
4345
if (task.Status == TaskStatus.RanToCompletion)
4446
{
4547
return task;
4648
}
49+
4750
return task.CatchImpl(() => continuation(new CatchInfo<TResult>(task)).Task, cancellationToken);
4851
}
4952

@@ -55,8 +58,6 @@ private static Task<TResult> CatchImpl<TResult>(this Task task, Func<Task<TResul
5558
// Stay on the same thread if we can
5659
if (task.IsCompleted)
5760
{
58-
Contract.Assert(task.IsFaulted || task.IsCanceled); // caller ensures
59-
6061
if (task.IsFaulted)
6162
{
6263
try
@@ -80,8 +81,12 @@ private static Task<TResult> CatchImpl<TResult>(this Task task, Func<Task<TResul
8081
{
8182
return TaskHelpers.Canceled<TResult>();
8283
}
83-
84-
Contract.Assert(task.Status != TaskStatus.RanToCompletion);
84+
if (task.Status == TaskStatus.RanToCompletion)
85+
{
86+
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>();
87+
tcs.TrySetFromTask(task);
88+
return tcs.Task;
89+
}
8590
}
8691

8792
// Split into a continuation method so that we don't create a closure unnecessarily
@@ -242,7 +247,7 @@ internal static Task Finally(this Task task, Action continuation)
242247
{
243248
// Stay on the same thread if we can
244249
if (task.IsCompleted)
245-
{
250+
{
246251
try
247252
{
248253
continuation();
@@ -251,7 +256,7 @@ internal static Task Finally(this Task task, Action continuation)
251256
catch (Exception ex)
252257
{
253258
return TaskHelpers.FromError(ex);
254-
}
259+
}
255260
}
256261

257262
// Split into a continuation method so that we don't create a closure unnecessarily
@@ -267,7 +272,7 @@ internal static Task<TResult> Finally<TResult>(this Task<TResult> task, Action c
267272
{
268273
// Stay on the same thread if we can
269274
if (task.IsCompleted)
270-
{
275+
{
271276
try
272277
{
273278
continuation();
@@ -276,7 +281,7 @@ internal static Task<TResult> Finally<TResult>(this Task<TResult> task, Action c
276281
catch (Exception ex)
277282
{
278283
return TaskHelpers.FromError<TResult>(ex);
279-
}
284+
}
280285
}
281286

282287
// Split into a continuation method so that we don't create a closure unnecessarily

0 commit comments

Comments
 (0)