.NET Fiddle

CSharp · Console

/Program.cs

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;

static class Program
{
    public static void Main()
    {
        var cts = new CancellationTokenSource();
        cts.Token.Register(() => Console.WriteLine("Token Canceled"));

        var options = new ExecutionDataflowBlockOptions()
        {
            BoundedCapacity = 1,
            CancellationToken = cts.Token,
        };

        var block1 = new TransformBlock<char, char>(item =>
        {
            Console.WriteLine($"Block-1 processing {item}");
            return item;
        });

        var block2 = new TransformBlock<char, char>(item =>
        {
            Console.WriteLine($"Block-2 processing {item}");
            return item;
        }, options);

        var block3 = new ActionBlock<char>(item =>
        {
            Console.WriteLine($"Block-3 processing {item}");
            Thread.Sleep(100);
            throw new Exception();
        }, options);

        block1.LinkTo(block2, new() { PropagateCompletion = true });
        block2.LinkTo(block3, new() { PropagateCompletion = true });
        block1.OnErrorCancel(cts);
        block2.OnErrorCancel(cts);
        block3.OnErrorCancel(cts);

        block1.Post('A');
        block1.Post('B');
        block1.Post('C');
        block1.Complete();

        Task whenAll = Task.WhenAll(block1.Completion, block2.Completion, block3.Completion);
        try { whenAll.Wait(1000); } catch { }
        Console.WriteLine("Task.WhenAll " + (whenAll.IsCompleted ? "Completed" : "Timed-out"));
    }
	
    static async void OnErrorCancel(this IDataflowBlock block, CancellationTokenSource cts)
    {
        try { await block.Completion.ConfigureAwait(false); } catch { }
        if (block.Completion.IsFaulted) cts.Cancel();
    }
}
;