/// <summary> /// Reads input and applies the method to each input, and emits the output /// </summary> /// <param name="method">The worker method to apply to each element.</param> /// <typeparam name="TInput">The input type parameter.</typeparam> /// <typeparam name="TOutput">The output type parameter.</typeparam> private static Task Worker <TInput, TOutput>(Func <TInput, TOutput> method) { return(AutomationExtensions.RunTask( new { input = ChannelMarker.ForRead <TInput>(WORKERINPUT), output = ChannelMarker.ForWrite <TOutput>(WORKEROUTPUT) }, async self => { try { while (true) { await self.output.WriteAsync(method(await self.input.ReadAsync().ConfigureAwait(false))).ConfigureAwait(false); } } catch (Exception ex) { if (!(ex is RetiredException)) { Console.WriteLine("ex: {0}", ex); } throw; } } )); }
public void TestRetireWithoutLoss() { Task[] tasks; int count = 0; using (new ChannelScope()) { tasks = new Task[] { AutomationExtensions.RunTask( new { channel = ChannelMarker.ForWrite <int>(CHANNEL_NAME) }, async self => { await Task.Delay(500); await self.channel.WriteAsync(1); } ), AutomationExtensions.RunTask( new { channel = ChannelMarker.ForWrite <int>(CHANNEL_NAME) }, async self => { await Task.Delay(1000); await self.channel.WriteAsync(1); } ), AutomationExtensions.RunTask( new { channel = ChannelMarker.ForRead <int>(CHANNEL_NAME) }, async self => { while (true) { await self.channel.ReadAsync(); count++; } } ) }; } var all = Task.WhenAll(tasks).WaitForTask(); if (count != 2) { throw new Exception(string.Format("Unexpected count, expected {0} but got {1}", 2, count)); } if (all.IsFaulted || !all.IsCompleted) { throw new Exception("Unexpected task state"); } }
/// <summary> /// Emits all values from the enumerable into the network /// </summary> /// <param name="values">Values.</param> /// <typeparam name="TInput">The 1st type parameter.</typeparam> private static Task Generator <TInput>(IEnumerable <TInput> values) { return(AutomationExtensions.RunTask( new { channel = ChannelMarker.ForWrite <TInput>(WORKERINPUT) }, async self => { foreach (var value in values) { await self.channel.WriteAsync(value).ConfigureAwait(false); } } )); }
private void TestReaderOverflow(QueueOverflowStrategy strategy) { using (new IsolatedChannelScope()) { var readertasks = Enumerable.Range(0, 4).Select(count => AutomationExtensions.RunTask(new { Input = ChannelMarker.ForRead <int>("channel", maxPendingReaders: 3, pendingReadersOverflowStrategy: strategy) }, async x => { //Console.WriteLine("Started {0}", count); while (true) { await x.Input.ReadAsync(); } }) ).ToList(); using (ChannelManager.GetChannel <int>("channel").AsWriteOnly()) Task.Delay(500).WaitForTaskOrThrow(); Task.WhenAny(readertasks.Union(new [] { Task.Delay(1000) })).WaitForTaskOrThrow(); Task.Delay(500).WaitForTaskOrThrow(); int discard; switch (strategy) { case QueueOverflowStrategy.FIFO: discard = 0; break; case QueueOverflowStrategy.LIFO: discard = readertasks.Count - 2; break; case QueueOverflowStrategy.Reject: default: discard = readertasks.Count - 1; break; } Assert.IsTrue(readertasks[discard].IsFaulted); TestAssert.IsInstanceOf <ChannelOverflowException>(readertasks[discard].Exception.Flatten().InnerExceptions.First()); readertasks.RemoveAt(discard); Assert.IsTrue(readertasks.All(x => x.IsCompleted && !x.IsFaulted && !x.IsCanceled)); } }
/// <summary> /// Collects input and combines it with the join method /// </summary> /// <param name="joinmethod">The method used to join results.</param> /// <param name="initial">The initial input to the join method, aka. the neutral element.</param> /// <typeparam name="TOutput">The type parameter for the data to join.</typeparam> /// <typeparam name="TResult">The type parameter for the aggregated data.</typeparam> private static async Task <TResult> Collector <TOutput, TResult>(Func <TResult, TOutput, TResult> joinmethod, TResult initial) { var current = initial; await AutomationExtensions.RunTask( new { channel = ChannelMarker.ForRead <TOutput>(WORKEROUTPUT) }, async self => { while (true) { current = joinmethod(current, await self.channel.ReadAsync().ConfigureAwait(false)); } } ).ConfigureAwait(false); return(current); }
public void TestScope() { var values = new[] { 0, 1, 2, 3, 4 }; var counter = new CounterShim(); var readercount = 10; var name = "bcast"; using (new IsolatedChannelScope()) { var writer = AutomationExtensions.RunTask( new { chan = ChannelMarker.ForWrite <int>(name, broadcast: true, initialBroadcastBarrier: readercount) }, async self => { foreach (var v in values) { await self.chan.WriteAsync(v); } } ); var readers = Enumerable.Range(0, readercount).Select(x => AutomationExtensions.RunTask( new { chan = ChannelMarker.ForRead <int>(name) }, async self => { foreach (var v in values) { var r = await self.chan.ReadAsync(); counter.Increment(); if (Comparer <int> .Default.Compare(v, r) != 0) { throw new Exception(string.Format("Got {0} but expected {1}", r, v)); } } } )).ToArray(); Task.WhenAll(readers.Union(new[] { writer })).WaitForTaskOrThrow(); if (counter.Count != readercount * values.Length) { throw new Exception(string.Format("The counter said {0} values were read, but {1} was expected", counter.Count, readercount * values.Length)); } } }
private void TestCappedPool(int poolsize, int readers, int writes) { var concurrent = 0; var max_concurrent = 0; var rnd = new Random(); var earlyRetire = new TaskCompletionSource <bool>(); using (new IsolatedChannelScope()) using (new ExecutionScope(poolsize <= 0 ? ThreadPool.DEFAULT_THREADPOOL : new CappedThreadedThreadPool(poolsize))) { var readertasks = Task.WhenAll(Enumerable.Range(0, readers).Select(count => AutomationExtensions.RunTask(new { Input = ChannelMarker.ForRead <int>("channel") }, async x => { //Console.WriteLine("Started {0}", count); while (true) { await x.Input.ReadAsync(); var cur = System.Threading.Interlocked.Increment(ref concurrent); //Console.WriteLine("Active {0}", count); // Dirty access to "concurrent" and "max_concurrent" variables max_concurrent = Math.Max(cur, Math.Max(max_concurrent, concurrent)); if (cur > poolsize && poolsize > 0) { Console.WriteLine("Found {0} concurrent threads", cur); earlyRetire.TrySetException(new Exception(string.Format("Found {0} concurrent threads", cur))); throw new Exception(string.Format("Found {0} concurrent threads", cur)); } // By blocking the actual thread, we provoke the threadpool to start multiple threads System.Threading.Thread.Sleep(rnd.Next(10, 500)); // Dirty access to "concurrent" and "max_concurrent" variables max_concurrent = Math.Max(cur, Math.Max(max_concurrent, concurrent)); System.Threading.Interlocked.Decrement(ref concurrent); //Console.WriteLine("Inactive {0}", count); } }) )); var writetask = AutomationExtensions.RunTask(new { Output = ChannelMarker.ForWrite <int>("channel") }, async x => { foreach (var i in Enumerable.Range(0, writes)) { //Console.WriteLine("Writing {0}", i); await x.Output.WriteAsync(i); } }); var timeout = Task.Delay((writes * 500) + 5000); if (Task.WhenAny(Task.WhenAll(readertasks, writetask), timeout, earlyRetire.Task).WaitForTaskOrThrow() == timeout) { throw new TimeoutException("I've waited for so long ...."); } Console.WriteLine("Threads at shutdown: {0}", concurrent); ExecutionScope.Current.EnsureFinishedAsync(TimeSpan.FromSeconds(5)).WaitForTaskOrThrow(); Console.WriteLine("Max concurrent threads: {0}, should be {1}", max_concurrent, poolsize <= 0 ? "unlimited" : poolsize.ToString()); } }