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."); }
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."); }
//[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")); } }
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); }
//[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")); } }