Skip to content

Commit

Permalink
Correctly propagate faulted status and exception from child to parent…
Browse files Browse the repository at this point in the history
… in attached case
  • Loading branch information
garuma committed Apr 13, 2012
1 parent 989100d commit a3ed899
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 4 deletions.
15 changes: 11 additions & 4 deletions mcs/class/corlib/System.Threading.Tasks/Task.cs
Expand Up @@ -455,8 +455,8 @@ internal void ChildCompleted (AggregateException childEx)
}

if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
Status = TaskStatus.RanToCompletion;
ProcessChildExceptions ();
Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
ProcessCompleteDelegates ();
if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
parent.ChildCompleted (this.Exception);
Expand All @@ -475,8 +475,10 @@ void InnerInvoke ()
internal void Finish ()
{
// If there was children created and they all finished, we set the countdown
if (childTasks != null)
childTasks.Signal ();
if (childTasks != null) {
if (childTasks.Signal ())
ProcessChildExceptions (true);
}

// Don't override Canceled or Faulted
if (status == TaskStatus.Running) {
Expand Down Expand Up @@ -512,7 +514,7 @@ void ProcessCompleteDelegates ()
}
}

void ProcessChildExceptions ()
void ProcessChildExceptions (bool isParent = false)
{
if (exSlot == null || exSlot.ChildExceptions == null)
return;
Expand All @@ -523,6 +525,11 @@ void ProcessChildExceptions ()
AggregateException childEx;
while (exSlot.ChildExceptions.TryDequeue (out childEx))
exSlot.Exception.AddChildException (childEx);

if (isParent) {
Status = TaskStatus.Faulted;
ProcessCompleteDelegates ();
}
}
#endregion

Expand Down
86 changes: 86 additions & 0 deletions mcs/class/corlib/Test/System.Threading.Tasks/TaskTest.cs
Expand Up @@ -816,6 +816,92 @@ public void CanceledContinuationExecuteSynchronouslyTest ()
Assert.IsTrue (thrown);
}

[Test]
public void WhenChildTaskErrorIsThrownParentTaskShouldBeFaulted ()
{
Task innerTask = null;
var testTask = new Task (() =>
{
innerTask = new Task (() =>
{
throw new InvalidOperationException ();
}, TaskCreationOptions.AttachedToParent);
innerTask.RunSynchronously ();
});
testTask.RunSynchronously ();

Assert.AreNotEqual (TaskStatus.Running, testTask.Status);
Assert.IsNotNull (innerTask);
Assert.IsTrue (innerTask.IsFaulted);
Assert.IsNotNull (testTask.Exception);
Assert.IsTrue (testTask.IsFaulted);
Assert.IsNotNull (innerTask.Exception);
}

[Test]
public void WhenChildTaskErrorIsThrownOnlyOnFaultedContinuationShouldExecute ()
{
var continuationRan = false;
var testTask = new Task (() =>
{
var task = new Task (() =>
{
throw new InvalidOperationException();
}, TaskCreationOptions.AttachedToParent);
task.RunSynchronously ();
});
var onErrorTask = testTask.ContinueWith (x => continuationRan = true, TaskContinuationOptions.OnlyOnFaulted);
testTask.RunSynchronously ();
onErrorTask.Wait (100);
Assert.IsTrue (continuationRan);
}

[Test]
public void WhenChildTaskErrorIsThrownNotOnFaultedContinuationShouldNotBeExecuted ()
{
var continuationRan = false;
var testTask = new Task (() =>
{
var task = new Task (() =>
{
throw new InvalidOperationException();
}, TaskCreationOptions.AttachedToParent);
task.RunSynchronously();
});
var onErrorTask = testTask.ContinueWith (x => continuationRan = true, TaskContinuationOptions.NotOnFaulted);
testTask.RunSynchronously ();
Assert.IsTrue (onErrorTask.IsCompleted);
Assert.IsFalse (onErrorTask.IsFaulted);
Assert.IsFalse (continuationRan);
}

[Test]
public void WhenChildTaskSeveralLevelsDeepHandlesAggregateExceptionErrorStillBubblesToParent ()
{
var continuationRan = false;
AggregateException e = null;
var testTask = new Task (() =>
{
var child1 = new Task (() =>
{
var child2 = new Task (() =>
{
throw new InvalidOperationException();
}, TaskCreationOptions.AttachedToParent);
child2.RunSynchronously ();
}, TaskCreationOptions.AttachedToParent);
child1.RunSynchronously();
e = child1.Exception;
child1.Exception.Handle (ex => true);
});
var onErrorTask = testTask.ContinueWith (x => continuationRan = true, TaskContinuationOptions.OnlyOnFaulted);
testTask.RunSynchronously ();
onErrorTask.Wait (100);
Assert.IsNotNull (e);
Assert.IsTrue (continuationRan);
}

#if NET_4_5
[Test]
public void Delay_Invalid ()
Expand Down

0 comments on commit a3ed899

Please sign in to comment.