diff --git a/mcs/class/corlib/System.Threading.Tasks/Task.cs b/mcs/class/corlib/System.Threading.Tasks/Task.cs index d3a59f9db5654..052a7b2963caa 100644 --- a/mcs/class/corlib/System.Threading.Tasks/Task.cs +++ b/mcs/class/corlib/System.Threading.Tasks/Task.cs @@ -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); @@ -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) { @@ -512,7 +514,7 @@ void ProcessCompleteDelegates () } } - void ProcessChildExceptions () + void ProcessChildExceptions (bool isParent = false) { if (exSlot == null || exSlot.ChildExceptions == null) return; @@ -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 diff --git a/mcs/class/corlib/Test/System.Threading.Tasks/TaskTest.cs b/mcs/class/corlib/Test/System.Threading.Tasks/TaskTest.cs index 26ad5ad9f5121..d4d3f261838fd 100644 --- a/mcs/class/corlib/Test/System.Threading.Tasks/TaskTest.cs +++ b/mcs/class/corlib/Test/System.Threading.Tasks/TaskTest.cs @@ -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 ()