예제 #1
0
        public void RunTransformManyBlockConformanceTests()
        {
            bool passed = true;

            #region Sync
            {
                // Do everything twice - once through OfferMessage and Once through Post
                for (FeedMethod feedMethod = FeedMethod._First; passed& feedMethod < FeedMethod._Count; feedMethod++)
                {
                    Func <DataflowBlockOptions, TargetProperties <int> > transformManyBlockFactory =
                        options =>
                    {
                        TransformManyBlock <int, int> transformManyBlock = new TransformManyBlock <int, int>(i => new[] { i }, (ExecutionDataflowBlockOptions)options);
                        ActionBlock <int>             actionBlock        = new ActionBlock <int>(i => TrackCaptures(i), (ExecutionDataflowBlockOptions)options);

                        transformManyBlock.LinkTo(actionBlock);

                        return(new TargetProperties <int> {
                            Target = transformManyBlock, Capturer = actionBlock, ErrorVerifyable = false
                        });
                    };
                    CancellationTokenSource cancellationSource = new CancellationTokenSource();
                    var defaultOptions = new ExecutionDataflowBlockOptions();
                    var dopOptions     = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    };
                    var mptOptions = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 10
                    };
                    var cancellationOptions = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 100, CancellationToken = cancellationSource.Token
                    };

                    passed &= FeedTarget(transformManyBlockFactory, defaultOptions, 1, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, defaultOptions, 10, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, dopOptions, 1000, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, mptOptions, 10000, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, mptOptions, 10000, Intervention.Complete, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, cancellationOptions, 10000, Intervention.Cancel, cancellationSource, feedMethod, true);
                }

                // Test chained Post/Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformManyBlock <int, int>, int>(4, () => new TransformManyBlock <int, int>(i => new[] { i * 2 }));
                    for (int i = 0; i < ITERS; i++)
                    {
                        network.Post(i);
                        localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i * 16);
                    }
                    Console.WriteLine("{0}: Chained Post/Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained SendAsync/Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformManyBlock <int, int>, int>(4, () => new TransformManyBlock <int, int>(i => new[] { i * 2 }));
                    for (int i = 0; i < ITERS; i++)
                    {
                        network.SendAsync(i);
                        localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i * 16);
                    }
                    Console.WriteLine("{0}: Chained SendAsync/Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained Post all then Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformManyBlock <int, int>, int>(4, () => new TransformManyBlock <int, int>(i => new[] { i * 2 }));
                    for (int i = 0; i < ITERS; i++)
                    {
                        localPassed &= network.Post(i) == true;
                    }
                    for (int i = 0; i < ITERS; i++)
                    {
                        localPassed &= ((IReceivableSourceBlock <int>)network).Receive() == i * 16;
                    }
                    Console.WriteLine("{0}: Chained Post all then Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained SendAsync all then Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformManyBlock <int, int>, int>(4, () => new TransformManyBlock <int, int>(i => new[] { i * 2 }));
                    var       tasks       = new Task[ITERS];
                    for (int i = 1; i <= ITERS; i++)
                    {
                        tasks[i - 1] = network.SendAsync(i);
                    }
                    Task.WaitAll(tasks);
                    int total = 0;
                    for (int i = 1; i <= ITERS; i++)
                    {
                        total += ((IReceivableSourceBlock <int>)network).Receive();
                    }
                    localPassed &= (total == ((ITERS * (ITERS + 1)) / 2 * 16));
                    Console.WriteLine("{0}: Chained SendAsync all then Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test multiple yielded results
                {
                    bool localPassed = true;

                    var t = new TransformManyBlock <int, int>(i =>
                    {
                        return(Enumerable.Range(0, 10));
                    });
                    t.Post(42);
                    t.Complete();
                    for (int i = 0; i < 10; i++)
                    {
                        localPassed &= t.Receive() == i;
                    }
                    t.Completion.Wait();
                    Console.WriteLine("{0}: Test multiple yielded results", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test that OperationCanceledExceptions are ignored
                {
                    bool localPassed = true;

                    var t = new TransformManyBlock <int, int>(i =>
                    {
                        if ((i % 2) == 0)
                        {
                            throw new OperationCanceledException();
                        }
                        return(new[] { i });
                    });
                    for (int i = 0; i < 10; i++)
                    {
                        t.Post(i);
                    }
                    t.Complete();
                    for (int i = 0; i < 10; i++)
                    {
                        if ((i % 2) != 0)
                        {
                            localPassed &= t.Receive() == i;
                        }
                    }
                    t.Completion.Wait();
                    Console.WriteLine("{0}: OperationCanceledExceptions are ignored", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test using a precanceled token
                {
                    bool localPassed = true;
                    try
                    {
                        var cts = new CancellationTokenSource();
                        cts.Cancel();
                        var dbo = new ExecutionDataflowBlockOptions {
                            CancellationToken = cts.Token
                        };
                        var t = new TransformManyBlock <int, int>(i => new[] { i }, dbo);

                        int         ignoredValue;
                        IList <int> ignoredValues;
                        localPassed &= t.LinkTo(new ActionBlock <int>(delegate { })) != null;
                        localPassed &= t.SendAsync(42).Result == false;
                        localPassed &= t.TryReceiveAll(out ignoredValues) == false;
                        localPassed &= t.Post(42) == false;
                        localPassed &= t.OutputCount == 0;
                        localPassed &= t.TryReceive(out ignoredValue) == false;
                        localPassed &= t.Completion != null;
                        t.Complete();
                    }
                    catch (Exception)
                    {
                        localPassed = false;
                    }
                    Console.WriteLine("      > {0}: Precanceled tokens work correctly", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test faulting
                {
                    bool localPassed = true;
                    var  t           = new TransformManyBlock <int, int>(new Func <int, IEnumerable <int> >(i => { throw new InvalidOperationException(); }));
                    t.Post(42);
                    t.Post(1);
                    t.Post(2);
                    t.Post(3);
                    try { t.Completion.Wait(); }
                    catch { }
                    localPassed &= t.Completion.IsFaulted;
                    localPassed &= SpinWait.SpinUntil(() => t.InputCount == 0, 500);
                    localPassed &= SpinWait.SpinUntil(() => t.OutputCount == 0, 500);
                    localPassed &= t.Post(4) == false;
                    Console.WriteLine("      > {0}: Faulted handled correctly", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test reuse of a list and array
                {
                    bool localPassed = true;
                    foreach (bool bounded in new[] { false, true })
                    {
                        for (int dop = 1; dop < Environment.ProcessorCount; dop++)
                        {
                            var dbo = bounded ?
                                      new ExecutionDataflowBlockOptions {
                                MaxDegreeOfParallelism = dop, BoundedCapacity = 2
                            } :
                            new ExecutionDataflowBlockOptions {
                                MaxDegreeOfParallelism = dop
                            };
                            foreach (IList <int> list in new IList <int>[]
                            {
                                new int[1],
                                new List <int>()
                                {
                                    0
                                },
                                new Collection <int>()
                                {
                                    0
                                }
                            })
                            {
                                int nextExpectedValue = 1;
                                TransformManyBlock <int, int> tmb1 = null;
                                tmb1 = new TransformManyBlock <int, int>(i =>
                                {
                                    if (i == 1000)
                                    {
                                        tmb1.Complete();
                                        return((IEnumerable <int>)null);
                                    }
                                    else if (dop == 1)
                                    {
                                        list[0] = i + 1;
                                        return((IEnumerable <int>)list);
                                    }
                                    else if (list is int[])
                                    {
                                        return(new int[1] {
                                            i + 1
                                        });
                                    }
                                    else if (list is List <int> )
                                    {
                                        return(new List <int>()
                                        {
                                            i + 1
                                        });
                                    }
                                    else
                                    {
                                        return new Collection <int>()
                                        {
                                            i + 1
                                        }
                                    };
                                }, dbo);
                                TransformBlock <int, int> tmb2 = new TransformBlock <int, int>(i =>
                                {
                                    if (i != nextExpectedValue)
                                    {
                                        localPassed = false;

                                        tmb1.Complete();
                                    }
                                    nextExpectedValue++;
                                    return(i);
                                });
                                tmb1.LinkTo(tmb2);
                                tmb2.LinkTo(tmb1);
                                tmb1.SendAsync(0).Wait();
                                tmb1.Completion.Wait();
                            }
                        }
                    }
                    Console.WriteLine("      > {0}: Reuse of a list and array", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test throwing an OCE
                {
                    bool localPassed = true;
                    foreach (bool bounded in new[] { true, false })
                    {
                        for (int dop = 1; dop < Environment.ProcessorCount; dop++)
                        {
                            var dbo = bounded ?
                                      new ExecutionDataflowBlockOptions {
                                MaxDegreeOfParallelism = dop, BoundedCapacity = 2
                            } :
                            new ExecutionDataflowBlockOptions {
                                MaxDegreeOfParallelism = dop
                            };

                            foreach (int mode in new[] { 0, 1, 2 })
                            {
                                const int ITERS = 50;
                                var       mres  = new ManualResetEventSlim();
                                var       tmb   = new TransformManyBlock <int, int>(i =>
                                {
                                    if (i < ITERS - 1)
                                    {
                                        throw new OperationCanceledException();
                                    }
                                    if (mode == 0)
                                    {
                                        return new int[] { i }
                                    }
                                    ;
                                    else if (mode == 1)
                                    {
                                        return new List <int>()
                                        {
                                            i
                                        }
                                    }
                                    ;
                                    else
                                    {
                                        return(Enumerable.Repeat(i, 1));
                                    }
                                }, dbo);
                                var ab = new ActionBlock <int>(i =>
                                {
                                    if (i != ITERS - 1)
                                    {
                                        localPassed = false;
                                    }
                                    mres.Set();
                                });
                                tmb.LinkTo(ab);
                                for (int i = 0; i < ITERS; i++)
                                {
                                    tmb.SendAsync(i).Wait();
                                }
                                mres.Wait();
                            }
                        }
                    }
                    Console.WriteLine("{0}: Canceled invocation", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }
            }
            #endregion

            #region Async
            {
                // Do everything twice - once through OfferMessage and Once through Post
                for (FeedMethod feedMethod = FeedMethod._First; passed& feedMethod < FeedMethod._Count; feedMethod++)
                {
                    Func <DataflowBlockOptions, TargetProperties <int> > transformManyBlockFactory =
                        options =>
                    {
                        TransformManyBlock <int, int> transformManyBlock = new TransformManyBlock <int, int>(i => Task.Run(() => (IEnumerable <int>) new[] { i }), (ExecutionDataflowBlockOptions)options);
                        ActionBlock <int>             actionBlock        = new ActionBlock <int>(i => TrackCaptures(i), (ExecutionDataflowBlockOptions)options);

                        transformManyBlock.LinkTo(actionBlock);

                        return(new TargetProperties <int> {
                            Target = transformManyBlock, Capturer = actionBlock, ErrorVerifyable = false
                        });
                    };
                    CancellationTokenSource cancellationSource = new CancellationTokenSource();
                    var defaultOptions = new ExecutionDataflowBlockOptions();
                    var dopOptions     = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    };
                    var mptOptions = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 10
                    };
                    var cancellationOptions = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 100, CancellationToken = cancellationSource.Token
                    };

                    passed &= FeedTarget(transformManyBlockFactory, defaultOptions, 1, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, defaultOptions, 10, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, dopOptions, 1000, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, mptOptions, 10000, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, mptOptions, 10000, Intervention.Complete, null, feedMethod, true);
                    passed &= FeedTarget(transformManyBlockFactory, cancellationOptions, 10000, Intervention.Cancel, cancellationSource, feedMethod, true);
                }

                // Test chained Post/Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformManyBlock <int, int>, int>(4, () => new TransformManyBlock <int, int>(i => Task.Run(() => (IEnumerable <int>) new[] { i * 2 })));
                    for (int i = 0; i < ITERS; i++)
                    {
                        network.Post(i);
                        localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i * 16);
                    }
                    Console.WriteLine("{0}: Chained Post/Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained SendAsync/Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformManyBlock <int, int>, int>(4, () => new TransformManyBlock <int, int>(i => Task.Run(() => (IEnumerable <int>) new[] { i * 2 })));
                    for (int i = 0; i < ITERS; i++)
                    {
                        network.SendAsync(i);
                        localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i * 16);
                    }
                    Console.WriteLine("{0}: Chained SendAsync/Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained Post all then Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformManyBlock <int, int>, int>(4, () => new TransformManyBlock <int, int>(i => Task.Run(() => (IEnumerable <int>) new[] { i * 2 })));
                    for (int i = 0; i < ITERS; i++)
                    {
                        localPassed &= network.Post(i) == true;
                    }
                    for (int i = 0; i < ITERS; i++)
                    {
                        localPassed &= ((IReceivableSourceBlock <int>)network).Receive() == i * 16;
                    }
                    Console.WriteLine("{0}: Chained Post all then Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained SendAsync all then Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformManyBlock <int, int>, int>(4, () => new TransformManyBlock <int, int>(i => Task.Run(() => (IEnumerable <int>) new[] { i * 2 })));
                    var       tasks       = new Task[ITERS];
                    for (int i = 1; i <= ITERS; i++)
                    {
                        tasks[i - 1] = network.SendAsync(i);
                    }
                    Task.WaitAll(tasks);
                    int total = 0;
                    for (int i = 1; i <= ITERS; i++)
                    {
                        total += ((IReceivableSourceBlock <int>)network).Receive();
                    }
                    localPassed &= (total == ((ITERS * (ITERS + 1)) / 2 * 16));
                    Console.WriteLine("{0}: Chained SendAsync all then Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test multiple yielded results
                {
                    bool localPassed = true;

                    var t = new TransformManyBlock <int, int>(i => Task.Run(() => (IEnumerable <int>)Enumerable.Range(0, 10).ToArray()));
                    t.Post(42);
                    t.Complete();
                    for (int i = 0; i < 10; i++)
                    {
                        localPassed &= t.Receive() == i;
                    }
                    t.Completion.Wait();
                    Console.WriteLine("{0}: Test multiple yielded results", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test that OperationCanceledExceptions are ignored
                {
                    bool localPassed = true;

                    var t = new TransformManyBlock <int, int>(i =>
                    {
                        if ((i % 2) == 0)
                        {
                            throw new OperationCanceledException();
                        }
                        return(new[] { i });
                    });
                    for (int i = 0; i < 10; i++)
                    {
                        t.Post(i);
                    }
                    t.Complete();
                    for (int i = 0; i < 10; i++)
                    {
                        if ((i % 2) != 0)
                        {
                            localPassed &= t.Receive() == i;
                        }
                    }
                    t.Completion.Wait();
                    Console.WriteLine("{0}: OperationCanceledExceptions are ignored", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test that null tasks are ignored
                {
                    bool localPassed = true;

                    var t = new TransformManyBlock <int, int>(i =>
                    {
                        if ((i % 2) == 0)
                        {
                            return(null);
                        }
                        return(Task.Run(() => (IEnumerable <int>) new[] { i }));
                    });
                    for (int i = 0; i < 10; i++)
                    {
                        t.Post(i);
                    }
                    t.Complete();
                    for (int i = 0; i < 10; i++)
                    {
                        if ((i % 2) != 0)
                        {
                            localPassed &= t.Receive() == i;
                        }
                    }
                    t.Completion.Wait();
                    Console.WriteLine("{0}: OperationCanceledExceptions are ignored", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test that null tasks are ignored when a reordering buffer is in place
                {
                    bool localPassed = true;

                    var t = new TransformManyBlock <int, int>(new Func <int, Task <IEnumerable <int> > >(i =>
                    {
                        if (i == 0)
                        {
                            Task.Delay(1000).Wait();
                            return(null);
                        }
                        return(Task.Run(() => (IEnumerable <int>) new[] { i }));
                    }), new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = 2
                    });
                    t.Post(0);
                    t.Post(1);
                    try
                    {
                        localPassed &= t.Receive(TimeSpan.FromSeconds(4)) == 1;
                    }
                    catch
                    {
                        localPassed = false;
                    }
                    Console.WriteLine("{0}: null tasks are ignored with reordering buffer", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test faulting from the delegate
                {
                    bool localPassed = true;
                    var  t           = new TransformManyBlock <int, int>(new Func <int, Task <IEnumerable <int> > >(i => { throw new InvalidOperationException(); }));
                    t.Post(42);
                    t.Post(1);
                    t.Post(2);
                    t.Post(3);
                    try { t.Completion.Wait(); }
                    catch { }
                    localPassed &= t.Completion.IsFaulted;
                    localPassed &= SpinWait.SpinUntil(() => t.InputCount == 0, 500);
                    localPassed &= SpinWait.SpinUntil(() => t.OutputCount == 0, 500);
                    localPassed &= t.Post(4) == false;
                    Console.WriteLine("      > {0}: Faulted from delegate handled correctly", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test faulting from the task
                {
                    bool localPassed = true;
                    var  t           = new TransformManyBlock <int, int>(new Func <int, Task <IEnumerable <int> > >(i => Task.Run <IEnumerable <int> >(new Func <IEnumerable <int> >(() => { throw new InvalidOperationException(); }))));
                    t.Post(42);
                    t.Post(1);
                    t.Post(2);
                    t.Post(3);
                    try { t.Completion.Wait(); }
                    catch { }
                    localPassed &= t.Completion.IsFaulted;
                    localPassed &= SpinWait.SpinUntil(() => t.InputCount == 0, 500);
                    localPassed &= SpinWait.SpinUntil(() => t.OutputCount == 0, 500);
                    localPassed &= t.Post(4) == false;
                    Console.WriteLine("      > {0}: Faulted from task handled correctly", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test reuse of a list and array
                {
                    bool localPassed = true;
                    foreach (bool bounded in new[] { false, true })
                    {
                        for (int dop = 1; dop < Environment.ProcessorCount; dop++)
                        {
                            var dbo = bounded ?
                                      new ExecutionDataflowBlockOptions {
                                MaxDegreeOfParallelism = dop, BoundedCapacity = 2
                            } :
                            new ExecutionDataflowBlockOptions {
                                MaxDegreeOfParallelism = dop
                            };
                            foreach (IList <int> list in new IList <int>[]
                            {
                                new int[1],
                                new List <int>()
                                {
                                    0
                                },
                                new Collection <int>()
                                {
                                    0
                                }
                            })
                            {
                                int nextExpectedValue = 1;
                                TransformManyBlock <int, int> tmb1 = null;
                                tmb1 = new TransformManyBlock <int, int>(i =>
                                {
                                    return(Task.Run(() =>
                                    {
                                        if (i == 1000)
                                        {
                                            tmb1.Complete();
                                            return (IEnumerable <int>)null;
                                        }
                                        else if (dop == 1)
                                        {
                                            list[0] = i + 1;
                                            return (IEnumerable <int>)list;
                                        }
                                        else if (list is int[])
                                        {
                                            return new int[1] {
                                                i + 1
                                            };
                                        }
                                        else if (list is List <int> )
                                        {
                                            return new List <int>()
                                            {
                                                i + 1
                                            };
                                        }
                                        else
                                        {
                                            return new Collection <int>()
                                            {
                                                i + 1
                                            }
                                        };
                                    }));
                                }, dbo);
                                TransformBlock <int, int> tmb2 = new TransformBlock <int, int>(i =>
                                {
                                    if (i != nextExpectedValue)
                                    {
                                        localPassed = false;
                                        tmb1.Complete();
                                    }
                                    nextExpectedValue++;
                                    return(i);
                                });
                                tmb1.LinkTo(tmb2);
                                tmb2.LinkTo(tmb1);
                                tmb1.SendAsync(0).Wait();
                                tmb1.Completion.Wait();
                            }
                        }
                    }
                    Console.WriteLine("      > {0}: Reuse of a list and array", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test throwing an OCE
                {
                    bool localPassed = true;
                    foreach (bool bounded in new[] { true, false })
                    {
                        for (int dop = 1; dop < Environment.ProcessorCount; dop++)
                        {
                            var dbo = bounded ?
                                      new ExecutionDataflowBlockOptions {
                                MaxDegreeOfParallelism = dop, BoundedCapacity = 2
                            } :
                            new ExecutionDataflowBlockOptions {
                                MaxDegreeOfParallelism = dop
                            };

                            foreach (int mode in new[] { 0, 1, 2 })
                            {
                                const int ITERS = 50;
                                var       mres  = new ManualResetEventSlim();
                                var       tmb   = new TransformManyBlock <int, int>(i =>
                                {
                                    var cts = new CancellationTokenSource();
                                    return(Task.Run(() =>
                                    {
                                        if (i < ITERS - 1)
                                        {
                                            cts.Cancel();

                                            cts.Token.ThrowIfCancellationRequested();
                                        }
                                        if (mode == 0)
                                        {
                                            return new int[] { i }
                                        }
                                        ;
                                        else if (mode == 1)
                                        {
                                            return new List <int>()
                                            {
                                                i
                                            }
                                        }
                                        ;
                                        else
                                        {
                                            return Enumerable.Repeat(i, 1);
                                        }
                                    }, cts.Token));
                                }, dbo);
                                var ab = new ActionBlock <int>(i =>
                                {
                                    if (i != ITERS - 1)
                                    {
                                        localPassed = false;
                                    }
                                    mres.Set();
                                });
                                tmb.LinkTo(ab);
                                for (int i = 0; i < ITERS; i++)
                                {
                                    tmb.SendAsync(i).Wait();
                                }
                                mres.Wait();
                            }
                        }
                    }
                    Console.WriteLine("      > {0}: Canceled invocation", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }
            }
            #endregion

            Assert.True(passed, "Test failed.");
        }
예제 #2
0
        public void RunTransformBlockConformanceTests()
        {
            bool passed = true;

            // SYNC
            #region Sync
            {
                // Do everything twice - once through OfferMessage and Once through Post
                for (FeedMethod feedMethod = FeedMethod._First; passed& feedMethod < FeedMethod._Count; feedMethod++)
                {
                    Func <DataflowBlockOptions, TargetProperties <int> > transformBlockFactory =
                        options =>
                    {
                        TransformBlock <int, int> transformBlock = new TransformBlock <int, int>(i => i, (ExecutionDataflowBlockOptions)options);
                        ActionBlock <int>         actionBlock    = new ActionBlock <int>(i => TrackCaptures(i), (ExecutionDataflowBlockOptions)options);

                        transformBlock.LinkTo(actionBlock);

                        return(new TargetProperties <int> {
                            Target = transformBlock, Capturer = actionBlock, ErrorVerifyable = false
                        });
                    };
                    CancellationTokenSource cancellationSource = new CancellationTokenSource();
                    var defaultOptions = new ExecutionDataflowBlockOptions();
                    var dopOptions     = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    };
                    var mptOptions = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 2
                    };
                    var cancellationOptions = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 2, CancellationToken = cancellationSource.Token
                    };

                    passed &= FeedTarget(transformBlockFactory, defaultOptions, 1, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, defaultOptions, 1, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, dopOptions, 1, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, mptOptions, 1, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, mptOptions, 1, Intervention.Complete, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, cancellationOptions, 1, Intervention.Cancel, cancellationSource, feedMethod, true);
                }

                // Test chained Post/Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformBlock <int, int>, int>(4, () => new TransformBlock <int, int>(i => i * 2));
                    for (int i = 0; i < ITERS; i++)
                    {
                        network.Post(i);
                        localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i * 16);
                    }
                    Console.WriteLine("{0}: Chained Post/Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained SendAsync/Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformBlock <int, int>, int>(4, () => new TransformBlock <int, int>(i => i * 2));
                    for (int i = 0; i < ITERS; i++)
                    {
                        network.SendAsync(i);
                        localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i * 16);
                    }
                    Console.WriteLine("{0}: Chained SendAsync/Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained Post all then Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformBlock <int, int>, int>(4, () => new TransformBlock <int, int>(i => i * 2));
                    for (int i = 0; i < ITERS; i++)
                    {
                        localPassed &= network.Post(i) == true;
                    }
                    for (int i = 0; i < ITERS; i++)
                    {
                        localPassed &= ((IReceivableSourceBlock <int>)network).Receive() == i * 16;
                    }
                    Console.WriteLine("{0}: Chained Post all then Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained SendAsync all then Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformBlock <int, int>, int>(4, () => new TransformBlock <int, int>(i => i * 2));
                    var       tasks       = new Task[ITERS];
                    for (int i = 1; i <= ITERS; i++)
                    {
                        tasks[i - 1] = network.SendAsync(i);
                    }
                    Task.WaitAll(tasks);
                    int total = 0;
                    for (int i = 1; i <= ITERS; i++)
                    {
                        total += ((IReceivableSourceBlock <int>)network).Receive();
                    }
                    localPassed &= (total == ((ITERS * (ITERS + 1)) / 2 * 16));
                    Console.WriteLine("{0}: Chained SendAsync all then Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test that OperationCanceledExceptions are ignored
                {
                    bool localPassed = true;

                    var t = new TransformBlock <int, int>(i =>
                    {
                        if ((i % 2) == 0)
                        {
                            throw new OperationCanceledException();
                        }
                        return(i);
                    });
                    for (int i = 0; i < 2; i++)
                    {
                        t.Post(i);
                    }
                    t.Complete();
                    for (int i = 0; i < 2; i++)
                    {
                        if ((i % 2) != 0)
                        {
                            localPassed &= t.Receive() == i;
                        }
                    }
                    t.Completion.Wait();
                    Console.WriteLine("{0}: OperationCanceledExceptions are ignored", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test using a precanceled token
                {
                    bool localPassed = true;
                    try
                    {
                        var cts = new CancellationTokenSource();
                        cts.Cancel();
                        var dbo = new ExecutionDataflowBlockOptions {
                            CancellationToken = cts.Token
                        };
                        var t = new TransformBlock <int, int>(i => i, dbo);

                        int         ignoredValue;
                        IList <int> ignoredValues;
                        localPassed &= t.LinkTo(new ActionBlock <int>(delegate { })) != null;
                        localPassed &= t.SendAsync(42).Result == false;
                        localPassed &= t.TryReceiveAll(out ignoredValues) == false;
                        localPassed &= t.Post(42) == false;
                        localPassed &= t.OutputCount == 0;
                        localPassed &= t.TryReceive(out ignoredValue) == false;
                        localPassed &= t.Completion != null;
                        t.Complete();
                    }
                    catch (Exception)
                    {
                        localPassed = false;
                    }
                    Console.WriteLine("    {0}: Precanceled tokens work correctly", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test faulting
                {
                    bool localPassed = true;
                    var  t           = new TransformBlock <int, int>(new Func <int, int>(i => { throw new InvalidOperationException(); }));
                    t.Post(42);
                    t.Post(1);
                    t.Post(2);
                    t.Post(3);
                    try { t.Completion.Wait(); }
                    catch { }
                    localPassed &= t.Completion.IsFaulted;
                    localPassed &= SpinWait.SpinUntil(() => t.InputCount == 0, 500);
                    localPassed &= SpinWait.SpinUntil(() => t.OutputCount == 0, 500);
                    localPassed &= t.Post(4) == false;
                    Console.WriteLine("    {0}: Faulted handled correctly", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }
            }
            #endregion

            #region Async
            // ASYNC (a copy of the sync but with constructors returning Task<T> instead of T
            {
                // Do everything twice - once through OfferMessage and Once through Post
                for (FeedMethod feedMethod = FeedMethod._First; passed& feedMethod < FeedMethod._Count; feedMethod++)
                {
                    Func <DataflowBlockOptions, TargetProperties <int> > transformBlockFactory =
                        options =>
                    {
                        TransformBlock <int, int> transformBlock = new TransformBlock <int, int>(i => Task.Run(() => i), (ExecutionDataflowBlockOptions)options);
                        ActionBlock <int>         actionBlock    = new ActionBlock <int>(i => TrackCaptures(i), (ExecutionDataflowBlockOptions)options);

                        transformBlock.LinkTo(actionBlock);

                        return(new TargetProperties <int> {
                            Target = transformBlock, Capturer = actionBlock, ErrorVerifyable = false
                        });
                    };
                    CancellationTokenSource cancellationSource = new CancellationTokenSource();
                    var defaultOptions = new ExecutionDataflowBlockOptions();
                    var dopOptions     = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount
                    };
                    var mptOptions = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 2
                    };
                    var cancellationOptions = new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 2, CancellationToken = cancellationSource.Token
                    };

                    passed &= FeedTarget(transformBlockFactory, defaultOptions, 1, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, dopOptions, 10, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, mptOptions, 10000, Intervention.None, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, mptOptions, 10000, Intervention.Complete, null, feedMethod, true);
                    passed &= FeedTarget(transformBlockFactory, cancellationOptions, 10000, Intervention.Cancel, cancellationSource, feedMethod, true);
                }

                // Test chained Post/Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformBlock <int, int>, int>(4, () => new TransformBlock <int, int>(i => Task.Run(() => i * 2)));
                    for (int i = 0; i < ITERS; i++)
                    {
                        network.Post(i);
                        localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i * 16);
                    }
                    Console.WriteLine("{0}: Chained Post/Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained SendAsync/Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformBlock <int, int>, int>(4, () => new TransformBlock <int, int>(i => Task.Run(() => i * 2)));
                    for (int i = 0; i < ITERS; i++)
                    {
                        network.SendAsync(i);
                        localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i * 16);
                    }
                    Console.WriteLine("{0}: Chained SendAsync/Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained Post all then Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformBlock <int, int>, int>(4, () => new TransformBlock <int, int>(i => Task.Run(() => i * 2)));
                    for (int i = 0; i < ITERS; i++)
                    {
                        localPassed &= network.Post(i) == true;
                    }
                    for (int i = 0; i < ITERS; i++)
                    {
                        localPassed &= ((IReceivableSourceBlock <int>)network).Receive() == i * 16;
                    }
                    Console.WriteLine("{0}: Chained Post all then Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test chained SendAsync all then Receive
                {
                    bool      localPassed = true;
                    const int ITERS       = 2;
                    var       network     = Chain <TransformBlock <int, int>, int>(4, () => new TransformBlock <int, int>(i => Task.Run(() => i * 2)));
                    var       tasks       = new Task[ITERS];
                    for (int i = 1; i <= ITERS; i++)
                    {
                        tasks[i - 1] = network.SendAsync(i);
                    }
                    Task.WaitAll(tasks);
                    int total = 0;
                    for (int i = 1; i <= ITERS; i++)
                    {
                        total += ((IReceivableSourceBlock <int>)network).Receive();
                    }
                    localPassed &= (total == ((ITERS * (ITERS + 1)) / 2 * 16));
                    Console.WriteLine("{0}: Chained SendAsync all then Receive", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test that OperationCanceledExceptions are ignored
                {
                    bool localPassed = true;

                    var t = new TransformBlock <int, int>(i =>
                    {
                        if ((i % 2) == 0)
                        {
                            throw new OperationCanceledException();
                        }
                        return(Task.Run(() => i));
                    });
                    for (int i = 0; i < 2; i++)
                    {
                        t.Post(i);
                    }
                    t.Complete();
                    for (int i = 0; i < 2; i++)
                    {
                        if ((i % 2) != 0)
                        {
                            localPassed &= t.Receive() == i;
                        }
                    }
                    t.Completion.Wait();
                    Console.WriteLine("{0}: OperationCanceledExceptions are ignored", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test that null tasks are ignored
                {
                    bool localPassed = true;

                    var t = new TransformBlock <int, int>(i =>
                    {
                        if ((i % 2) == 0)
                        {
                            return(null);
                        }
                        return(Task.Run(() => i));
                    });
                    for (int i = 0; i < 2; i++)
                    {
                        t.Post(i);
                    }
                    t.Complete();
                    for (int i = 0; i < 2; i++)
                    {
                        if ((i % 2) != 0)
                        {
                            localPassed &= t.Receive() == i;
                        }
                    }
                    t.Completion.Wait();
                    Console.WriteLine("{0}: null tasks are ignored", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test that null tasks are ignored when a reordering buffer is in place
                {
                    bool localPassed = true;

                    var t = new TransformBlock <int, int>(i =>
                    {
                        if (i == 0)
                        {
                            Task.Delay(10).Wait();
                            return(null);
                        }
                        return(Task.Run(() => i));
                    }, new ExecutionDataflowBlockOptions {
                        MaxDegreeOfParallelism = 2
                    });
                    t.Post(0);
                    t.Post(1);
                    try
                    {
                        localPassed &= t.Receive(TimeSpan.FromSeconds(4)) == 1;
                    }
                    catch
                    {
                        localPassed = false;
                    }
                    Console.WriteLine("{0}: null tasks are ignored with reordering buffer", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test faulting from the delegate
                {
                    bool localPassed = true;
                    var  t           = new TransformBlock <int, int>(new Func <int, Task <int> >(i => { throw new InvalidOperationException(); }));
                    t.Post(42);
                    t.Post(1);
                    t.Post(2);
                    t.Post(3);
                    try { t.Completion.Wait(); }
                    catch { }
                    localPassed &= t.Completion.IsFaulted;
                    localPassed &= SpinWait.SpinUntil(() => t.InputCount == 0, 500);
                    localPassed &= SpinWait.SpinUntil(() => t.OutputCount == 0, 500);
                    localPassed &= t.Post(4) == false;
                    Console.WriteLine("    {0}: Faulted from delegate handled correctly", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }

                // Test faulting from the task
                {
                    bool localPassed = true;
                    var  t           = new TransformBlock <int, int>(new Func <int, Task <int> >(i => Task.Run(new Func <int>(() => { throw new InvalidOperationException(); }))));
                    t.Post(42);
                    t.Post(1);
                    t.Post(2);
                    t.Post(3);
                    try { t.Completion.Wait(); }
                    catch { }
                    localPassed &= t.Completion.IsFaulted;
                    localPassed &= SpinWait.SpinUntil(() => t.InputCount == 0, 500);
                    localPassed &= SpinWait.SpinUntil(() => t.OutputCount == 0, 500);
                    localPassed &= t.Post(4) == false;
                    Console.WriteLine("    {0}: Faulted from task handled correctly", localPassed ? "Success" : "Failure");
                    passed &= localPassed;
                }
            }
            #endregion

            Assert.True(passed, "Test failed.");
        }
예제 #3
0
        //[Fact(Skip = "Outerloop")]
        public void RunActionBlockConformanceTests()
        {
            // SYNC
            // Do everything twice - once through OfferMessage and Once through Post
            for (FeedMethod feedMethod = FeedMethod._First; feedMethod < FeedMethod._Count; feedMethod++)
            {
                Func <DataflowBlockOptions, TargetProperties <int> > actionBlockFactory =
                    options =>
                {
                    ITargetBlock <int> target = new ActionBlock <int>(i => TrackCaptures(i), (ExecutionDataflowBlockOptions)options);
                    return(new TargetProperties <int> {
                        Target = target, Capturer = target, ErrorVerifyable = true
                    });
                };

                CancellationTokenSource cancellationSource = new CancellationTokenSource();
                var defaultOptions = new ExecutionDataflowBlockOptions();
                var dopOptions     = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount
                };
                var mptOptions = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 1
                };
                var cancellationOptions = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 1, CancellationToken = cancellationSource.Token
                };
                var spscOptions = new ExecutionDataflowBlockOptions {
                    SingleProducerConstrained = true
                };
                var spscMptOptions = new ExecutionDataflowBlockOptions {
                    SingleProducerConstrained = true, MaxMessagesPerTask = 10
                };

                Assert.True(FeedTarget(actionBlockFactory, defaultOptions, 1, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, dopOptions, 1, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, mptOptions, 1, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, mptOptions, 1, Intervention.Complete, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, cancellationOptions, 1, Intervention.Cancel, cancellationSource, feedMethod, true));

                Assert.True(FeedTarget(actionBlockFactory, spscOptions, 1, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, spscOptions, 1, Intervention.Complete, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, spscMptOptions, 1, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, spscMptOptions, 1, Intervention.Complete, null, feedMethod, true));
            }

            // Test scheduler usage
            {
                bool localPassed = true;
                for (int trial = 0; trial < 2; trial++)
                {
                    var sts = new SimpleTaskScheduler();

                    var options = new ExecutionDataflowBlockOptions {
                        TaskScheduler = sts, MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded, MaxMessagesPerTask = 1
                    };
                    if (trial == 0)
                    {
                        options.SingleProducerConstrained = true;
                    }

                    var ab = new ActionBlock <int>(i => localPassed &= TaskScheduler.Current.Id == sts.Id, options);
                    for (int i = 0; i < 2; i++)
                    {
                        ab.Post(i);
                    }
                    ab.Complete();
                    ab.Completion.Wait();
                }

                Assert.True(localPassed, string.Format("{0}: Correct scheduler usage", localPassed ? "Success" : "Failure"));
            }

            // Test count
            {
                bool localPassed = true;
                for (int trial = 0; trial < 2; trial++)
                {
                    var barrier1 = new Barrier(2);
                    var barrier2 = new Barrier(2);
                    var ab       = new ActionBlock <int>(i =>
                    {
                        barrier1.SignalAndWait();
                        barrier2.SignalAndWait();
                    }, new ExecutionDataflowBlockOptions {
                        SingleProducerConstrained = (trial == 0)
                    });
                    for (int iter = 0; iter < 2; iter++)
                    {
                        for (int i = 1; i <= 2; i++)
                        {
                            ab.Post(i);
                        }
                        for (int i = 1; i >= 0; i--)
                        {
                            barrier1.SignalAndWait();
                            localPassed &= i == ab.InputCount;
                            barrier2.SignalAndWait();
                        }
                    }
                }

                Assert.True(localPassed, string.Format("{0}: InputCount", localPassed ? "Success" : "Failure"));
            }

            // Test ordering
            {
                bool localPassed = true;
                for (int trial = 0; trial < 2; trial++)
                {
                    int prev = -1;
                    var ab   = new ActionBlock <int>(i =>
                    {
                        if (prev + 1 != i)
                        {
                            localPassed &= false;
                        }
                        prev = i;
                    }, new ExecutionDataflowBlockOptions {
                        SingleProducerConstrained = (trial == 0)
                    });
                    for (int i = 0; i < 2; i++)
                    {
                        ab.Post(i);
                    }
                    ab.Complete();
                    ab.Completion.Wait();
                }

                Assert.True(localPassed, string.Format("{0}: Correct ordering", localPassed ? "Success" : "Failure"));
            }

            // Test non-greedy
            {
                bool localPassed = true;
                var  barrier     = new Barrier(2);
                var  ab          = new ActionBlock <int>(i =>
                {
                    barrier.SignalAndWait();
                }, new ExecutionDataflowBlockOptions {
                    BoundedCapacity = 1
                });
                ab.SendAsync(1);
                Task.Delay(200).Wait();
                var sa2 = ab.SendAsync(2);
                localPassed &= !sa2.IsCompleted;
                barrier.SignalAndWait(); // for SendAsync(1)
                barrier.SignalAndWait(); // for SendAsync(2)
                localPassed &= sa2.Wait(100);
                int total = 0;
                ab = new ActionBlock <int>(i =>
                {
                    Interlocked.Add(ref total, i);
                    Task.Delay(1).Wait();
                }, new ExecutionDataflowBlockOptions {
                    BoundedCapacity = 1
                });
                for (int i = 1; i <= 100; i++)
                {
                    ab.SendAsync(i);
                }
                SpinWait.SpinUntil(() => total == ((100 * 101) / 2), 30000);
                localPassed &= total == ((100 * 101) / 2);
                Assert.True(localPassed, string.Format("total={0} (must be {1})", total, (100 * 101) / 2));
                Assert.True(localPassed, string.Format("{0}: Non-greedy support", localPassed ? "Success" : "Failure"));
            }

            // Test that OperationCanceledExceptions are ignored
            {
                bool localPassed = true;
                for (int trial = 0; trial < 2; trial++)
                {
                    int sumOfOdds = 0;
                    var ab        = new ActionBlock <int>(i =>
                    {
                        if ((i % 2) == 0)
                        {
                            throw new OperationCanceledException();
                        }
                        sumOfOdds += i;
                    }, new ExecutionDataflowBlockOptions {
                        SingleProducerConstrained = (trial == 0)
                    });
                    for (int i = 0; i < 4; i++)
                    {
                        ab.Post(i);
                    }
                    ab.Complete();
                    ab.Completion.Wait();
                    localPassed = sumOfOdds == (1 + 3);
                }

                Assert.True(localPassed, string.Format("{0}: OperationCanceledExceptions are ignored", localPassed ? "Success" : "Failure"));
            }

            // Test using a precanceled token
            {
                bool localPassed = true;
                try
                {
                    var cts = new CancellationTokenSource();
                    cts.Cancel();
                    var dbo = new ExecutionDataflowBlockOptions {
                        CancellationToken = cts.Token
                    };
                    var ab = new ActionBlock <int>(i => { }, dbo);

                    localPassed &= ab.Post(42) == false;
                    localPassed &= ab.InputCount == 0;
                    localPassed &= ab.Completion != null;
                    ab.Complete();
                }
                catch (Exception)
                {
                    localPassed = false;
                }

                Assert.True(localPassed, string.Format("{0}: Precanceled tokens work correctly", localPassed ? "Success" : "Failure"));
            }

            // Test faulting
            {
                bool localPassed = true;
                for (int trial = 0; trial < 2; trial++)
                {
                    var ab = new ActionBlock <int>(i => { throw new InvalidOperationException(); },
                                                   new ExecutionDataflowBlockOptions {
                        SingleProducerConstrained = (trial == 0)
                    });
                    ab.Post(42);
                    ab.Post(1);
                    ab.Post(2);
                    ab.Post(3);
                    try { localPassed &= ab.Completion.Wait(5000); }
                    catch { }
                    localPassed &= ab.Completion.IsFaulted;
                    localPassed &= SpinWait.SpinUntil(() => ab.InputCount == 0, 500);
                    localPassed &= ab.Post(4) == false;
                }

                Assert.True(localPassed, string.Format("{0}: Faulted handled correctly", localPassed ? "Success" : "Failure"));
            }

            // ASYNC (a copy of the sync but with constructors returning Task instead of void

            // Do everything twice - once through OfferMessage and Once through Post
            for (FeedMethod feedMethod = FeedMethod._First; feedMethod < FeedMethod._Count; feedMethod++)
            {
                Func <DataflowBlockOptions, TargetProperties <int> > actionBlockFactory =
                    options =>
                {
                    ITargetBlock <int> target = new ActionBlock <int>(i => TrackCapturesAsync(i), (ExecutionDataflowBlockOptions)options);
                    return(new TargetProperties <int> {
                        Target = target, Capturer = target, ErrorVerifyable = true
                    });
                };
                CancellationTokenSource cancellationSource = new CancellationTokenSource();
                var defaultOptions = new ExecutionDataflowBlockOptions();
                var dopOptions     = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount
                };
                var mptOptions = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 10
                };
                var cancellationOptions = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 100, CancellationToken = cancellationSource.Token
                };

                Assert.True(FeedTarget(actionBlockFactory, defaultOptions, 1, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, defaultOptions, 10, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, dopOptions, 1000, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, mptOptions, 10000, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, mptOptions, 10000, Intervention.Complete, null, feedMethod, true));
                Assert.True(FeedTarget(actionBlockFactory, cancellationOptions, 10000, Intervention.Cancel, cancellationSource, feedMethod, true));
            }

            // Test scheduler usage
            {
                bool localPassed = true;
                var  sts         = new SimpleTaskScheduler();
                var  ab          = new ActionBlock <int>(i =>
                {
                    localPassed &= TaskScheduler.Current.Id == sts.Id;
                    return(Task.Factory.StartNew(() => { }));
                }, new ExecutionDataflowBlockOptions {
                    TaskScheduler = sts, MaxDegreeOfParallelism = -1, MaxMessagesPerTask = 10
                });
                for (int i = 0; i < 2; i++)
                {
                    ab.Post(i);
                }
                ab.Complete();
                ab.Completion.Wait();
                Assert.True(localPassed, string.Format("{0}: Correct scheduler usage", localPassed ? "Success" : "Failure"));
            }

            // Test count
            {
                bool localPassed = true;
                var  barrier1    = new Barrier(2);
                var  barrier2    = new Barrier(2);
                var  ab          = new ActionBlock <int>(i => Task.Factory.StartNew(() =>
                {
                    barrier1.SignalAndWait();
                    barrier2.SignalAndWait();
                }));
                for (int iter = 0; iter < 2; iter++)
                {
                    for (int i = 1; i <= 2; i++)
                    {
                        ab.Post(i);
                    }
                    for (int i = 1; i >= 0; i--)
                    {
                        barrier1.SignalAndWait();
                        localPassed &= i == ab.InputCount;
                        barrier2.SignalAndWait();
                    }
                }
                Assert.True(localPassed, string.Format("{0}: InputCount", localPassed ? "Success" : "Failure"));
            }

            // Test ordering
            {
                bool localPassed = true;
                int  prev        = -1;
                var  ab          = new ActionBlock <int>(i =>
                {
                    return(Task.Factory.StartNew(() =>
                    {
                        if (prev + 1 != i)
                        {
                            localPassed &= false;
                        }
                        prev = i;
                    }));
                });
                for (int i = 0; i < 2; i++)
                {
                    ab.Post(i);
                }
                ab.Complete();
                ab.Completion.Wait();
                Assert.True(localPassed, string.Format("{0}: Correct ordering", localPassed ? "Success" : "Failure"));
            }

            // Test non-greedy
            {
                bool localPassed = true;
                var  barrier     = new Barrier(2);
                var  ab          = new ActionBlock <int>(i =>
                {
                    return(Task.Factory.StartNew(() =>
                    {
                        barrier.SignalAndWait();
                    }));
                }, new ExecutionDataflowBlockOptions {
                    BoundedCapacity = 1
                });
                ab.SendAsync(1);
                Task.Delay(200).Wait();
                var sa2 = ab.SendAsync(2);
                localPassed &= !sa2.IsCompleted;
                barrier.SignalAndWait(); // for SendAsync(1)
                barrier.SignalAndWait(); // for SendAsync(2)
                localPassed &= sa2.Wait(100);
                int total = 0;
                ab = new ActionBlock <int>(i =>
                {
                    return(Task.Factory.StartNew(() =>
                    {
                        Interlocked.Add(ref total, i);
                        Task.Delay(1).Wait();
                    }));
                }, new ExecutionDataflowBlockOptions {
                    BoundedCapacity = 1
                });
                for (int i = 1; i <= 100; i++)
                {
                    ab.SendAsync(i);
                }
                SpinWait.SpinUntil(() => total == ((100 * 101) / 2), 30000);
                localPassed &= total == ((100 * 101) / 2);
                Assert.True(localPassed, string.Format("total={0} (must be {1})", total, (100 * 101) / 2));
                Assert.True(localPassed, string.Format("{0}: Non-greedy support", localPassed ? "Success" : "Failure"));
            }

            // Test that OperationCanceledExceptions are ignored
            {
                bool localPassed = true;
                int  sumOfOdds   = 0;
                var  ab          = new ActionBlock <int>(i =>
                {
                    if ((i % 2) == 0)
                    {
                        throw new OperationCanceledException();
                    }
                    return(Task.Factory.StartNew(() => { sumOfOdds += i; }));
                });
                for (int i = 0; i < 4; i++)
                {
                    ab.Post(i);
                }
                ab.Complete();
                ab.Completion.Wait();
                localPassed = sumOfOdds == (1 + 3);
                Assert.True(localPassed, string.Format("{0}: OperationCanceledExceptions are ignored", localPassed ? "Success" : "Failure"));
            }

            // Test that null task is ignored
            {
                bool localPassed = true;
                int  sumOfOdds   = 0;
                var  ab          = new ActionBlock <int>(i =>
                {
                    if ((i % 2) == 0)
                    {
                        return(null);
                    }
                    return(Task.Factory.StartNew(() => { sumOfOdds += i; }));
                });
                for (int i = 0; i < 4; i++)
                {
                    ab.Post(i);
                }
                ab.Complete();
                ab.Completion.Wait();
                localPassed = sumOfOdds == (1 + 3);
                Assert.True(localPassed, string.Format("{0}: null tasks are ignored", localPassed ? "Success" : "Failure"));
            }

            // Test faulting from the delegate
            {
                bool localPassed = true;
                var  ab          = new ActionBlock <int>(new Func <int, Task>(i => { throw new InvalidOperationException(); }));
                ab.Post(42);
                ab.Post(1);
                ab.Post(2);
                ab.Post(3);
                try { localPassed &= ab.Completion.Wait(100); }
                catch { }
                localPassed &= ab.Completion.IsFaulted;
                localPassed &= SpinWait.SpinUntil(() => ab.InputCount == 0, 500);
                localPassed &= ab.Post(4) == false;
                Assert.True(localPassed, string.Format("{0}: Faulted from delegate handled correctly", localPassed ? "Success" : "Failure"));
            }

            // Test faulting from the task
            {
                bool localPassed = true;
                var  ab          = new ActionBlock <int>(i => Task.Factory.StartNew(() => { throw new InvalidOperationException(); }));
                ab.Post(42);
                ab.Post(1);
                ab.Post(2);
                ab.Post(3);
                try { localPassed &= ab.Completion.Wait(100); }
                catch { }
                localPassed &= ab.Completion.IsFaulted;
                localPassed &= SpinWait.SpinUntil(() => ab.InputCount == 0, 500);
                localPassed &= ab.Post(4) == false;
                Assert.True(localPassed, string.Format("{0}: Faulted from task handled correctly", localPassed ? "Success" : "Failure"));
            }
        }
예제 #4
0
        protected static bool FeedTarget(Func <DataflowBlockOptions, TargetProperties <int> > targetFactory, DataflowBlockOptions options,
                                         int messageCount, Intervention intervention, CancellationTokenSource cancellationSource,
                                         FeedMethod feedMethod, bool isVerifiable)
        {
            bool passed = true;
            int  expectedCaptureCount            = messageCount;
            DataflowMessageStatus expectedStatus = DataflowMessageStatus.Accepted;
            bool expectedBoolStatus = true;

            // Aestetics
            var    maxDop     = options is ExecutionDataflowBlockOptions ? ((ExecutionDataflowBlockOptions)options).MaxDegreeOfParallelism : 1;
            string testHeader = string.Format("    + MessageCount={0}, MaxDOP={1}, MaxMPT={2}, Intervention={3}, FeedMethod={4}, Verifyable={5}",
                                              messageCount,
                                              maxDop,
                                              options.MaxMessagesPerTask, intervention, feedMethod, isVerifiable);

            // Initialize capturing structs
            s_captures = new int[messageCount];
            for (int j = 0; j < messageCount; j++)
            {
                s_captures[j] = -1;
            }
            s_captureCount = 0;
            s_currentDOP   = 0;
            s_maxDOP       = maxDop;


            // Construct target
            TargetProperties <int> targetProperties = targetFactory(options);
            ITargetBlock <int>     target           = targetProperties.Target;


            // Feeding loop
            for (int i = 0; i < messageCount; i++)
            {
                if (feedMethod == FeedMethod.OfferMessage)
                {
                    DataflowMessageStatus status = target.OfferMessage(new DataflowMessageHeader(1 + i), i, null, false); // Message ID doesn't metter because consumeToAccept:false
                    if (status != expectedStatus &&
                        (cancellationSource == null || !cancellationSource.IsCancellationRequested))                      // Ignore cancellation - it makes the expected result unpredictable
                    {
                        passed = false;
                        break;
                    }
                }
                else
                {
                    bool boolStatus = target.Post(i);
                    if (boolStatus != expectedBoolStatus &&
                        (cancellationSource == null || !cancellationSource.IsCancellationRequested)) // Ignore cancellation - it makes the expected result unpredictable
                    {
                        passed = false;
                        break;
                    }
                }

                // Intervene half way through.
                // While the intervention code is similar, the product behavior is different -
                // when DeclinePermanenetly is triggered, pending messages must still be processed, i.e.
                // the expectedCaptureCount is exactly i + 1;
                // when Cancel is triggered, pending messages may be discarded, i.e.
                // the expectedCaptureCount may be any number up to i + 1.
                if (i == messageCount / 2)
                {
                    switch (intervention)
                    {
                    case Intervention.Complete:
                        target.Complete();
                        expectedCaptureCount = i + 1;
                        expectedStatus       = DataflowMessageStatus.DecliningPermanently;
                        expectedBoolStatus   = false;
                        break;

                    case Intervention.Cancel:
                        cancellationSource.Cancel();
                        expectedCaptureCount = i + 1;
                        expectedStatus       = DataflowMessageStatus.DecliningPermanently;
                        expectedBoolStatus   = false;
                        break;
                    }
                }
            }


            // Tell the target we are done
            target.Complete();

            // Wait for the block to declare it is complete before starting verification
            try
            {
                target.Completion.Wait();
            }
            catch (AggregateException ae)
            {
                // Swallow OperationCancelledException.
                ae.Handle(e => e is OperationCanceledException);
            }

            // Tell the capturer we are done
            Task.Delay(1000).Wait();
            targetProperties.Capturer.Complete();

            // Wait for the block to declare it is complete before starting verification
            try
            {
                targetProperties.Capturer.Completion.Wait();
            }
            catch (AggregateException ae)
            {
                // Swallow OperationCancelledException.
                ae.Handle(e => e is OperationCanceledException);
            }


            // Verify total capture count.
            // s_captureCount is the actual number of captures.
            // expectedCaptureCount is the number of messages fed into the block before any intervention.
            // If there hasn't been intervention, that is the total number of messages.
            // When the intervention has been Complete, the actual capture count must match the input message count.
            // When the intervention has been Cancel, the actual capture count can be any number up to the input message count.
            if (isVerifiable && passed &&
                ((expectedCaptureCount != s_captureCount && intervention != Intervention.Cancel) ||
                 (expectedCaptureCount < s_captureCount && intervention == Intervention.Cancel)))
            {
                passed = false;
            }

            // When there's been cancellation, there might be gaps in the last maxDop captures.
            // So eliminate them from verification.
            s_captureCount -= maxDop;

            // Verify gaps in captures.
            // Each element of the capture array contains the ID of the task that reported the message with that payload.
            // Each element has been initialized to -1. If an element is still -1 after the run, then a message has not
            // been captured.
            if (isVerifiable && passed)
            {
                for (int i = 0; i < s_captureCount; i++)
                {
                    if (s_captures[i] < 0)
                    {
                        passed = false;
                        break;
                    }
                }
            }

            // Verify no task has exceeded MaxMessagesPerTask
            // Aggregate the elements of the capture array and count the occurences.
            // No count may exceed MMPT.
            if (isVerifiable && passed && options.MaxMessagesPerTask != -1)
            {
                var exceededMaxMPT = s_captures
                                     .Where(x => x >= 0)
                                     .GroupBy(x => x)
                                     .Where(g => g.Count() > options.MaxMessagesPerTask);

                foreach (var exceed in exceededMaxMPT)
                {
                    passed = false;
                }
            }

            return(passed);
        }
예제 #5
0
        //[Fact(Skip = "Outerloop")]
        public void RunBufferBlockConformanceTests()
        {
            bool localPassed;

            // Do everything twice - once through OfferMessage and Once through Post
            for (FeedMethod feedMethod = FeedMethod._First; feedMethod < FeedMethod._Count; feedMethod++)
            {
                Func <DataflowBlockOptions, TargetProperties <int> > bufferBlockFactory =
                    options =>
                {
                    BufferBlock <int> bufferBlock = new BufferBlock <int>(options);
                    ActionBlock <int> actionBlock = new ActionBlock <int>(i => TrackCaptures(i), (ExecutionDataflowBlockOptions)options);

                    bufferBlock.LinkTo(actionBlock);

                    return(new TargetProperties <int> {
                        Target = bufferBlock, Capturer = actionBlock, ErrorVerifyable = false
                    });
                };
                CancellationTokenSource cancellationSource = new CancellationTokenSource();
                var defaultOptions = new ExecutionDataflowBlockOptions();
                var dopOptions     = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount
                };
                var mptOptions = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 10
                };
                var cancellationOptions = new ExecutionDataflowBlockOptions {
                    MaxDegreeOfParallelism = Environment.ProcessorCount, MaxMessagesPerTask = 100, CancellationToken = cancellationSource.Token
                };

                Assert.True(FeedTarget(bufferBlockFactory, defaultOptions, 1, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(bufferBlockFactory, defaultOptions, 10, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(bufferBlockFactory, dopOptions, 1000, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(bufferBlockFactory, mptOptions, 10000, Intervention.None, null, feedMethod, true));
                Assert.True(FeedTarget(bufferBlockFactory, mptOptions, 10000, Intervention.Complete, null, feedMethod, true));
                Assert.True(FeedTarget(bufferBlockFactory, cancellationOptions, 10000, Intervention.Cancel, cancellationSource, feedMethod, true));
            }

            // Test chained Post/Receive
            {
                localPassed = true;
                const int ITERS   = 2;
                var       network = Chain <BufferBlock <int>, int>(4, () => new BufferBlock <int>());
                for (int i = 0; i < ITERS; i++)
                {
                    network.Post(i);
                    localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i);
                }
                Assert.True(localPassed, string.Format("{0}: Chained Post/Receive", localPassed ? "Success" : "Failure"));
            }

            // Test chained SendAsync/Receive
            {
                localPassed = true;
                const int ITERS   = 2;
                var       network = Chain <BufferBlock <int>, int>(4, () => new BufferBlock <int>());
                for (int i = 0; i < ITERS; i++)
                {
                    network.SendAsync(i);
                    localPassed &= (((IReceivableSourceBlock <int>)network).Receive() == i);
                }
                Assert.True(localPassed, string.Format("{0}: Chained SendAsync/Receive", localPassed ? "Success" : "Failure"));
            }

            // Test chained Post all then Receive
            {
                localPassed = true;
                const int ITERS   = 2;
                var       network = Chain <BufferBlock <int>, int>(4, () => new BufferBlock <int>());
                for (int i = 0; i < ITERS; i++)
                {
                    localPassed &= network.Post(i) == true;
                }
                for (int i = 0; i < ITERS; i++)
                {
                    localPassed &= ((IReceivableSourceBlock <int>)network).Receive() == i;
                }
                Assert.True(localPassed, string.Format("{0}: Chained Post all then Receive", localPassed ? "Success" : "Failure"));
            }

            // Test chained SendAsync all then Receive
            {
                localPassed = true;
                const int ITERS   = 2;
                var       network = Chain <BufferBlock <int>, int>(4, () => new BufferBlock <int>());
                var       tasks   = new Task[ITERS];
                for (int i = 1; i <= ITERS; i++)
                {
                    tasks[i - 1] = network.SendAsync(i);
                }
                Task.WaitAll(tasks);
                int total = 0;
                for (int i = 1; i <= ITERS; i++)
                {
                    total += ((IReceivableSourceBlock <int>)network).Receive();
                }
                localPassed &= (total == ((ITERS * (ITERS + 1)) / 2));
                Assert.True(localPassed, string.Format("{0}: Chained SendAsync all then Receive", localPassed ? "Success" : "Failure"));
            }

            // Test using a precanceled token
            {
                localPassed = true;
                try
                {
                    var cts = new CancellationTokenSource();
                    cts.Cancel();
                    var dbo = new DataflowBlockOptions {
                        CancellationToken = cts.Token
                    };
                    var bb = new BufferBlock <int>(dbo);

                    int         ignoredValue;
                    IList <int> ignoredValues;
                    localPassed &= bb.LinkTo(new ActionBlock <int>(delegate { })) != null;
                    localPassed &= bb.SendAsync(42).Result == false;
                    localPassed &= bb.TryReceiveAll(out ignoredValues) == false;
                    localPassed &= bb.Post(42) == false;
                    localPassed &= bb.Count == 0;
                    localPassed &= bb.TryReceive(out ignoredValue) == false;
                    localPassed &= bb.Completion != null;
                    bb.Complete();
                }
                catch (Exception)
                {
                    localPassed = false;
                }
                Assert.True(localPassed, string.Format("    {0}: Precanceled tokens work correctly", localPassed ? "Success" : "Failure"));
            }
        }