// This demonstrates the behavior of the WriteOnceBlock as it's easy to observe. // // Preface: I hate the name of this block. It's misleading. This should have been // called CloneBlock or CacheBlock. WriteOnceBlock sounds like you use the block once // and it then self-destructs. // How it works is simple, it will accept one published message and reject all other messages // coming to it. It will then take the first message and clone it and send it on every request // downstream. private static async Task WriteBlockBehaviorDemoAsync() { Console.WriteLine("WriteOneBlockDemo has started!"); var block = new WriteOnceBlock <string>(input => input); // needs a clone function for (int i = 0; i < 5; i++) { if (block.Post(ProduceTimeData())) { Console.WriteLine($"Message {i} was accepted"); } else { Console.WriteLine($"Message {i} was rejected"); } } // Notice the count is much higher than input count and that I am not // waiting on it to signal no more data is coming, as it always has data. for (int i = 0; i < 15; i++) { var output = await block.ReceiveAsync().ConfigureAwait(false); Console.WriteLine($"ReceivedMessage {i}: {output}"); } block.Complete(); await block.Completion.ConfigureAwait(false); Console.WriteLine("Finished!"); Console.ReadKey(); }
public IEnumerable <string> WriteOnceBlockUsage(int numberOfIteration, int minValue) { Console.WriteLine($"Inside {nameof(TplDataflow2BufferingBlocksController)} - {nameof(WriteOnceBlockUsage)}"); var strings = new BlockingCollection <string>(); // Create the members of the pipeline. var WriteOnceBlockGivenInputToASubscriber = new WriteOnceBlock <string>(null); var actionBlockSubscriber = new ActionBlock <string>(stringInput => Functions.AddInputIntoTheGivenList(strings, stringInput, "Subscriber") ); // Connect the dataflow blocks to form a pipeline. WriteOnceBlockGivenInputToASubscriber.LinkTo(actionBlockSubscriber, DataflowOptions.LinkOptions); // Start WriteOnceBlockUsage pipeline with the input values. for (var i = minValue; i <= minValue + numberOfIteration; i++) { WriteOnceBlockGivenInputToASubscriber.Post($"Value = {i}"); } // Mark the head of the pipeline as complete. WriteOnceBlockGivenInputToASubscriber.Complete(); // Wait for the last block in the pipeline to process all messages. actionBlockSubscriber.Completion.Wait(); return(strings); }
public async Task WriteOnceWithAction() { var block = new WriteOnceBlock <int>(a => a); var print = new ActionBlock <int>(a => Console.WriteLine($"Message: {a}")); block.LinkTo(print); for (int i = 0; i < 10; i++) { if (block.Post(i)) { Console.WriteLine($"Acceped {i}"); } else { Console.WriteLine($"Rejected {i}"); } } block.Complete(); await block.Completion; print.Complete(); await print.Completion; Console.WriteLine("Done!"); }
private static void TestSync4() { wb.LinkTo(displayBlock); wb.LinkTo(saveBlock); wb.LinkTo(sendBlock); for (int i = 0; i < 4; i++) { wb.Post(i); } wb.Complete(); Console.WriteLine("Post Finished"); wb.Completion.Wait(); Console.WriteLine("Process Finished"); Console.WriteLine("Recive:" + wb.Receive()); }
// Demo of it being used with other blocks. // // Only one push message propagates to the ActionBlock so only one message is received. private static async Task SimpleDemoAsync() { Console.WriteLine("WriteOneBlockDemo has started!"); var options = new DataflowLinkOptions { PropagateCompletion = true }; var writeBlock = new WriteOnceBlock <string>(input => input); var actionBlock = new ActionBlock <string>(Console.WriteLine); writeBlock.LinkTo(actionBlock, options); for (int i = 0; i < 5; i++) { await writeBlock.SendAsync(ProduceTimeData()).ConfigureAwait(false); } writeBlock.Complete(); await actionBlock.Completion.ConfigureAwait(false); Console.WriteLine("Finished!"); Console.ReadKey(); }
public void TestStatusAfterComplete() { bool passed = true; var writeOnce = new WriteOnceBlock <int>(x => x); writeOnce.Complete(); try { writeOnce.Completion.Wait(); Console.WriteLine("Completed without exception - Passed"); passed = writeOnce.Completion.Status == TaskStatus.RanToCompletion; Console.WriteLine("Status ({0}) - {1}", writeOnce.Completion.Status, passed ? "Passed" : "FAILED"); } catch (AggregateException ae) { ae.Handle(e => e is TaskCanceledException); passed = false; Console.WriteLine("Completed without exception - FAILED"); } Assert.True(passed, "Test failed."); }
public IDisposable LinkTo(ITargetBlock <IEnumerable <T> > target, DataflowLinkOptions linkOptions) { var emitter = new WriteOnceBlock <IEnumerable <T> >(items => items); // When the AccumBlock completes, send all the items to the emitter and complete it. AccumBlock.Completion.ContinueWith(async o => { if (o.IsFaulted) { ((ITargetBlock <IEnumerable <T> >)emitter).Fault(o.Exception); } else { await emitter.SendAsync(Accumulator); emitter.Complete(); Logger.LogTrace("emitter complete."); } }); Completion = emitter.Completion; var disposable = emitter.LinkTo(target, linkOptions); return(disposable); }
public async Task TestOutputAvailableAsync_DataAfterCompletion() { foreach (bool withUncanceledToken in DataflowTestHelpers.BooleanValues) foreach (bool withData in DataflowTestHelpers.BooleanValues) { var wob = new WriteOnceBlock<int>(_ => _); if (withData) wob.Post(42); else wob.Complete(); await wob.Completion; Task<bool> t = withUncanceledToken ? wob.OutputAvailableAsync(new CancellationTokenSource().Token) : wob.OutputAvailableAsync(); Assert.Equal(expected: withData, actual: await t); } }
public void RunWriteOnceBlockConformanceTests() { bool passed = true, localPassed = true; { // Test posting then receiving localPassed = true; var wob = new WriteOnceBlock <int>(i => i); int successfulPosts = 0; for (int i = 10; i <= 20; i++) { successfulPosts += wob.Post(i) ? 1 : 0; } localPassed |= successfulPosts == 1; localPassed |= wob.Receive() == 10; Console.WriteLine("{0}: Posting then receiving", localPassed ? "Success" : "Failure"); passed &= localPassed; } { // Test receiving then posting localPassed = true; var wob = new WriteOnceBlock <int>(i => i); Task.Factory.StartNew(() => { Task.Delay(1000).Wait(); wob.Post(42); }); localPassed |= wob.Receive() == 42; localPassed |= wob.Post(43) == false; wob.Completion.Wait(); Console.WriteLine("{0}: Receiving then posting", localPassed ? "Success" : "Failure"); passed &= localPassed; } { // Test broadcasting localPassed = true; var wob = new WriteOnceBlock <int>(i => i + 1); var tb1 = new TransformBlock <int, int>(i => i); var tb2 = new TransformBlock <int, int>(i => i); var tb3 = new TransformBlock <int, int>(i => i); wob.LinkTo(tb1); wob.LinkTo(tb2); wob.LinkTo(tb3); wob.Post(42); localPassed |= tb1.Receive() == 43; localPassed |= tb2.Receive() == 43; localPassed |= tb3.Receive() == 43; Console.WriteLine("{0}: Broadcasting", localPassed ? "Success" : "Failure"); passed &= localPassed; } { // Test using a precanceled token localPassed = true; try { var cts = new CancellationTokenSource(); cts.Cancel(); var dbo = new DataflowBlockOptions { CancellationToken = cts.Token }; var wob = new WriteOnceBlock <int>(i => i, dbo); int ignoredValue; IList <int> ignoredValues; localPassed &= wob.LinkTo(new ActionBlock <int>(delegate { })) != null; localPassed &= wob.SendAsync(42).Result == false; localPassed &= ((IReceivableSourceBlock <int>)wob).TryReceiveAll(out ignoredValues) == false; localPassed &= wob.Post(42) == false; localPassed &= wob.TryReceive(out ignoredValue) == false; localPassed &= wob.Completion != null; wob.Complete(); } catch (Exception) { localPassed = false; } Console.WriteLine("{0}: Precanceled tokens work correctly", localPassed ? "Success" : "Failure"); passed &= localPassed; } { // Test using token canceled after construction localPassed = true; try { var cts = new CancellationTokenSource(); var dbo = new DataflowBlockOptions { CancellationToken = cts.Token }; var wob = new WriteOnceBlock <int>(i => i, dbo); cts.Cancel(); int ignoredValue; IList <int> ignoredValues; localPassed &= wob.LinkTo(new ActionBlock <int>(delegate { })) != null; localPassed &= wob.SendAsync(42).Result == false; localPassed &= ((IReceivableSourceBlock <int>)wob).TryReceiveAll(out ignoredValues) == false; localPassed &= wob.Post(42) == false; localPassed &= wob.TryReceive(out ignoredValue) == false; localPassed &= wob.Completion != null; wob.Complete(); } catch (Exception) { localPassed = false; } Console.WriteLine("{0}: Precanceled tokens work correctly", localPassed ? "Success" : "Failure"); passed &= localPassed; } Assert.True(passed, "Test failed."); }
public void RunWriteOnceBlockConformanceTests() { bool passed = true, localPassed = true; { // Test posting then receiving localPassed = true; var wob = new WriteOnceBlock<int>(i => i); int successfulPosts = 0; for (int i = 10; i <= 20; i++) { successfulPosts += wob.Post(i) ? 1 : 0; } localPassed |= successfulPosts == 1; localPassed |= wob.Receive() == 10; Console.WriteLine("{0}: Posting then receiving", localPassed ? "Success" : "Failure"); passed &= localPassed; } { // Test receiving then posting localPassed = true; var wob = new WriteOnceBlock<int>(i => i); Task.Factory.StartNew(() => { Task.Delay(1000).Wait(); wob.Post(42); }); localPassed |= wob.Receive() == 42; localPassed |= wob.Post(43) == false; wob.Completion.Wait(); Console.WriteLine("{0}: Receiving then posting", localPassed ? "Success" : "Failure"); passed &= localPassed; } { // Test broadcasting localPassed = true; var wob = new WriteOnceBlock<int>(i => i + 1); var tb1 = new TransformBlock<int, int>(i => i); var tb2 = new TransformBlock<int, int>(i => i); var tb3 = new TransformBlock<int, int>(i => i); wob.LinkTo(tb1); wob.LinkTo(tb2); wob.LinkTo(tb3); wob.Post(42); localPassed |= tb1.Receive() == 43; localPassed |= tb2.Receive() == 43; localPassed |= tb3.Receive() == 43; Console.WriteLine("{0}: Broadcasting", localPassed ? "Success" : "Failure"); passed &= localPassed; } { // Test using a precanceled token localPassed = true; try { var cts = new CancellationTokenSource(); cts.Cancel(); var dbo = new DataflowBlockOptions { CancellationToken = cts.Token }; var wob = new WriteOnceBlock<int>(i => i, dbo); int ignoredValue; IList<int> ignoredValues; localPassed &= wob.LinkTo(new ActionBlock<int>(delegate { })) != null; localPassed &= wob.SendAsync(42).Result == false; localPassed &= ((IReceivableSourceBlock<int>)wob).TryReceiveAll(out ignoredValues) == false; localPassed &= wob.Post(42) == false; localPassed &= wob.TryReceive(out ignoredValue) == false; localPassed &= wob.Completion != null; wob.Complete(); } catch (Exception) { localPassed = false; } Console.WriteLine("{0}: Precanceled tokens work correctly", localPassed ? "Success" : "Failure"); passed &= localPassed; } { // Test using token canceled after construction localPassed = true; try { var cts = new CancellationTokenSource(); var dbo = new DataflowBlockOptions { CancellationToken = cts.Token }; var wob = new WriteOnceBlock<int>(i => i, dbo); cts.Cancel(); int ignoredValue; IList<int> ignoredValues; localPassed &= wob.LinkTo(new ActionBlock<int>(delegate { })) != null; localPassed &= wob.SendAsync(42).Result == false; localPassed &= ((IReceivableSourceBlock<int>)wob).TryReceiveAll(out ignoredValues) == false; localPassed &= wob.Post(42) == false; localPassed &= wob.TryReceive(out ignoredValue) == false; localPassed &= wob.Completion != null; wob.Complete(); } catch (Exception) { localPassed = false; } Console.WriteLine("{0}: Precanceled tokens work correctly", localPassed ? "Success" : "Failure"); passed &= localPassed; } Assert.True(passed, "Test failed."); }
public void TestStatusAfterComplete() { bool passed = true; var writeOnce = new WriteOnceBlock<int>(x => x); writeOnce.Complete(); try { writeOnce.Completion.Wait(); Console.WriteLine("Completed without exception - Passed"); passed = writeOnce.Completion.Status == TaskStatus.RanToCompletion; Console.WriteLine("Status ({0}) - {1}", writeOnce.Completion.Status, passed ? "Passed" : "FAILED"); } catch (AggregateException ae) { ae.Handle(e => e is TaskCanceledException); passed = false; Console.WriteLine("Completed without exception - FAILED"); } Assert.True(passed, "Test failed."); }
static void Main(string[] args) { var batchRequests = new BatchBlock <int>(batchSize: 10); BroadcastBlock <int> br = new BroadcastBlock <int>(x => x); ActionBlock <int> actionx = new ActionBlock <int>((i) => Console.WriteLine(i)); br.LinkTo(actionx, x => x > 10); MyProcessBlock pb = new MyProcessBlock(); pb.LinkTo(actionx); var sendToDb = new ActionBlock <int[]>(reqs => Console.WriteLine(reqs.Length)); batchRequests.LinkTo(sendToDb); for (int i = 0; i < 100; i++) { batchRequests.Post(i); } WriteOnceBlock <int> wo = new WriteOnceBlock <int>(x => x); ActionBlock <int> action = new ActionBlock <int>((i) => Console.WriteLine(i)); wo.LinkTo(action, new DataflowLinkOptions { PropagateCompletion = true }); wo.Post(1); wo.Post(2); wo.Post(3); wo.Post(4); wo.Post(5); wo.Complete(); BroadcastBlock <int> b = new BroadcastBlock <int>(x => x); TransformBlock <int, int> t2 = new TransformBlock <int, int>(x => x); TransformBlock <int, int> t = new TransformBlock <int, int>(x => x); JoinBlock <int, int> join = new JoinBlock <int, int>(); ActionBlock <Tuple <int, int> > action2 = new ActionBlock <Tuple <int, int> >((i) => Console.WriteLine("{0}-{1}", i.Item1, i.Item2), new ExecutionDataflowBlockOptions { BoundedCapacity = 10, MaxMessagesPerTask = 30 }); b.LinkTo(t, x => x > 10); b.LinkTo(t2); t.LinkTo(action, x => x > 10); t2.LinkTo(action, x => x > 10); t.LinkTo(join.Target1); t2.LinkTo(join.Target1); join.LinkTo(action2); DataFlowDebuggerSide.TestShowVisualizer(b); }
/// <summary> /// Connect to a target on first of bound proxies, or use ping based dynamic lookup /// </summary> /// <param name="address"></param> /// <param name="ct"></param> /// <returns></returns> public override async Task ConnectAsync(SocketAddress address, CancellationToken ct) { Info.Address = address; if (Info.Address.Family == AddressFamily.Bound) { // Unwrap proxy and connect address. If not bound, use local address to bind to. if (_boundEndpoint == null) { _boundEndpoint = address; // Unwrap bound address while (_boundEndpoint.Family == AddressFamily.Bound) { _boundEndpoint = ((BoundSocketAddress)_boundEndpoint).LocalAddress; } } Info.Address = ((BoundSocketAddress)Info.Address).RemoteAddress; } // // Get the named host from the registry if it exists - there should only be one... // This is the host we shall connect to. It can contain proxies to use as well. // var hostList = await Provider.NameService.LookupAsync( Info.Address.ToString(), NameRecordType.Host, ct).ConfigureAwait(false); Host = hostList.FirstOrDefault(); if (Host == null) { // If there is no host in the registry, create a fake host record for this address Host = new NameRecord(NameRecordType.Host, Info.Address.ToString()); } else { if (!Host.Name.Equals(Info.Address.ToString(), StringComparison.CurrentCultureIgnoreCase)) { // Translate the address to host address Info.Address = new ProxySocketAddress(Host.Name); } } // Commit all options that were set until now into info Info.Options.UnionWith(_optionCache.Select(p => Property <ulong> .Create( (uint)p.Key, p.Value))); // // Create tpl network for connect - prioritize input above errored attempts using // prioritized scheduling queue. // var retries = new CancellationTokenSource(); ct.Register(() => retries.Cancel()); var errors = new TransformBlock <DataflowMessage <INameRecord>, DataflowMessage <INameRecord> >( async(error) => { if (error.FaultCount > 0) { Host.RemoveReference(error.Arg.Address); await Provider.NameService.AddOrUpdateAsync(Host, retries.Token).ConfigureAwait(false); ProxyEventSource.Log.LinkFailure(this, error.Arg, Host, error.LastFault); } await Task.Delay((error.FaultCount + 1) * _throttleDelayMs, retries.Token).ConfigureAwait(false); return(error); }, new ExecutionDataflowBlockOptions { NameFormat = "Error (Connect) Id={1}", MaxDegreeOfParallelism = 2, // 2 parallel retries CancellationToken = retries.Token }); var linkAdapter = DataflowMessage <INameRecord> .CreateAdapter( new ExecutionDataflowBlockOptions { NameFormat = "Adapt (Connect) Id={1}", CancellationToken = ct, MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded, MaxMessagesPerTask = DataflowBlockOptions.Unbounded, SingleProducerConstrained = true, EnsureOrdered = false }); var linkQuery = Provider.NameService.Read( new ExecutionDataflowBlockOptions { NameFormat = "Query (Connect) Id={1}", CancellationToken = ct, EnsureOrdered = true }); var pinger = CreatePingBlock(errors, Info.Address, new ExecutionDataflowBlockOptions { NameFormat = "Ping (Connect) Id={1}", CancellationToken = ct, MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded, MaxMessagesPerTask = 1, EnsureOrdered = false }); var linker = CreateLinkBlock(errors, new ExecutionDataflowBlockOptions { NameFormat = "Link (Connect) Id={1}", CancellationToken = ct, MaxDegreeOfParallelism = 1, // Ensure one link is created at a time. MaxMessagesPerTask = DataflowBlockOptions.Unbounded, EnsureOrdered = false }); var connection = new WriteOnceBlock <IProxyLink>(l => l, new DataflowBlockOptions { NameFormat = "Final (Connect) Id={1}", MaxMessagesPerTask = 1, // Auto complete when link is created EnsureOrdered = false }); linkQuery.ConnectTo(linkAdapter); linkAdapter.ConnectTo(linker); errors.ConnectTo(pinger); pinger.ConnectTo(linker); linker.ConnectTo(connection); // // Now connect by starting the connection pipeline from query source... // if (_boundEndpoint != null) { // // User asked for specific set of proxies. Try linking with each // until we have a successful link. // await linkQuery.SendAsync(r => r.Matches(_boundEndpoint, NameRecordType.Proxy), ct).ConfigureAwait(false); } else { // // Consider all endpoints - if the host has a candidate list // use this list to directly link, and then ping remaining with // a throttle. Otherwise do a full ping. // var pingAdapter = DataflowMessage <INameRecord> .CreateAdapter( new ExecutionDataflowBlockOptions { NameFormat = "Any (Connect) Id={1}", CancellationToken = ct, MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded, MaxMessagesPerTask = DataflowBlockOptions.Unbounded, SingleProducerConstrained = true, EnsureOrdered = false }); var remaining = Provider.NameService.Read( new ExecutionDataflowBlockOptions { NameFormat = "Remaining (Connect) Id={1}", CancellationToken = ct, EnsureOrdered = true }); remaining.ConnectTo(pingAdapter); if (Host.References.Any()) { // Delay ping through errors path to give references time to link... pingAdapter.ConnectTo(errors); await linkQuery.SendAsync(r => r.Matches(Host.References, NameRecordType.Proxy), ct).ConfigureAwait(false); } else { // Send directly to ping pingAdapter.ConnectTo(pinger); } await remaining.SendAsync(r => r.IsProxyForHost(Info.Address), ct) .ConfigureAwait(false); } // Wait until a connected link is received. Then cancel the remainder of the pipeline. try { _link = await connection.ReceiveAsync(ct); connection.Complete(); retries.Cancel(); Host.AddReference(_link.Proxy.Address); Host.LastActivity = _link.Proxy.LastActivity = DateTime.Now; await Provider.NameService.AddOrUpdateAsync(_link.Proxy, ct).ConfigureAwait(false); } catch (Exception e) { throw SocketException.Create("Failed to connect", e); } finally { await Provider.NameService.AddOrUpdateAsync(Host, ct).ConfigureAwait(false); } }