4

For some reason when an OperationCanceledException gets thrown inside an IDataflowBlock, the block does not propagate this exception to its IDataflowBlock.Completion task. Running the code sample below returns an unexpected IDataflowBlock.Completion.Status == TaskStatus.RanToCompletion.

However, if the thrown exception type in the block is changed to an ArgumentNullException, the IDataflowBlock.Completion.Status changes to TaskStatus.Faulted and the exception is saved in its InnerException property.

Any ideas why OperationCanceledException is getting swallowed?

[TestFixture]
public class TplDataBlockExceptionTest
{
    [Test]
    public void ShouldThrowException()
    {
        // Arrange
        var block = new TransformBlock<int, string>(i =>
        {
            throw new OperationCanceledException();
            return i.ToString();
        });

        // Act

        block.Post(1);
        block.Complete();

        try
        {
            block.Completion.Wait();
        }
        catch (Exception)
        {
            // ignored
        }

        // Assert

        Assert.That(block.Completion.IsFaulted);
    }
}
Community
  • 1
  • 1

1 Answers1

5

I was able to reach Stephen Toub at Microsoft and he was able to confirm that this behavior is by design:

https://github.com/dotnet/corefx/blob/master/src/System.Threading.Tasks.Dataflow/src/Blocks/TransformBlock.cs#L186-L190

https://github.com/dotnet/corefx/blob/master/src/System.Threading.Tasks.Dataflow/src/Internal/Common.cs#L152-L175

  • 1
    It should be noted that this behavior is especially problematic when working with the `HttpClient` class, which throws `TaskCanceledException` instead of `TimeoutException` [for some unrelated reason](https://stackoverflow.com/questions/10547895/how-can-i-tell-when-httpclient-has-timed-out). – Theodor Zoulias Jun 26 '20 at 18:02