Beispiel #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CoCoL.SingleThreadedWorker"/> class.
 /// </summary>
 public SingleThreadedWorker()
 {
     AutomationExtensions.AutoWireChannels(this, null);
     m_channel      = ChannelManager.CreateChannel <Func <Task> >();
     m_workerSource = new CancellationTokenSource();
     m_worker       = AutomationExtensions.RunProtected(this, Start);
 }
Beispiel #2
0
        /// <summary>
        /// Sets up a simple process that distributes the input to the output channels
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="input">The data source.</param>
        /// <param name="output">The channels to distribute the data to.</param>
        /// <typeparam name="T">The 1st type parameter.</typeparam>
        public static Task ScatterAsync <T>(IReadChannel <T> input, IWriteChannel <T>[] output, ScatterGatherPolicy policy = ScatterGatherPolicy.Any)
        {
            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }
            if (output == null || output.Any(x => x == null))
            {
                throw new ArgumentNullException(nameof(output));
            }

            return(AutomationExtensions.RunTask(
                       new { Input = input, Output = output },
                       async self =>
            {
                if (policy == ScatterGatherPolicy.Any)
                {
                    while (true)
                    {
                        await MultiChannelAccess.WriteToAnyAsync(await self.Input.ReadAsync(), self.Output);
                    }
                }
                else
                {
                    var ix = 0;
                    while (true)
                    {
                        await self.Output[ix].WriteAsync(await self.Input.ReadAsync());
                        ix = (ix + 1) % output.Length;
                    }
                }
            }
                       ));
        }
Beispiel #3
0
        /// <summary>
        /// Runs a repeated parallel operation
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="source">The channel where requests are read from</param>
        /// <param name="handler">The method to invoke for each item</param>
        /// <param name="token">Token.</param>
        /// <param name="maxparallel">The maximum parallelism to use.</param>
        /// <param name="errorHandler">The error handler</param>
        /// <typeparam name="T">The type of data elements to handle</typeparam>
        public static Task RunParallelAsync <T>(IReadChannel <T> source, Func <T, Task> handler, System.Threading.CancellationToken token, int maxparallel = 10, Func <T, Exception, Task> errorHandler = null)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (maxparallel <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(maxparallel), maxparallel, "The size of the queue must be greater than zero");
            }

            return(AutomationExtensions.RunTask(
                       new
            {
                Requests = source
            },
                       async self =>
            {
                using (var tp = new TaskPool <T>(maxparallel, errorHandler))
                    while (true)
                    {
                        if (token.IsCancellationRequested)
                        {
                            throw new TaskCanceledException();
                        }
                        await tp.Run(await source.ReadAsync(), handler).ConfigureAwait(false);
                    }
            }));
        }
Beispiel #4
0
        /// <summary>
        /// Dispose the current instance
        /// </summary>
        /// <param name="isDisposing"><c>True</c> if disposing, false otherwise.</param>
        protected virtual void Dispose(bool isDisposing)
        {
            m_workerSource.Cancel();

            if (m_channel != null)
            {
                try { m_channel.Retire(); }
                catch { /* Ignore retire errors */ }
            }

            AutomationExtensions.RetireAllChannels(this);
        }
Beispiel #5
0
        /// <summary>
        /// Runs the process as a one-shot.
        /// </summary>
        /// <returns>The task.</returns>
        protected Task SingleRun()
        {
            if (m_started != null)
            {
                return(m_started);
            }

            lock (m_lock)
            {
                if (m_started == null)
                {
                    m_started = AutomationExtensions.RunProtected(this, Start);
                }
            }

            return(m_started);
        }
Beispiel #6
0
        /// <summary>
        /// Combines inputs from <paramref name="inputA"/> and <paramref name="inputB"/> and writes it to <paramref name="output"/>.
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="inputA">One input channel.</param>
        /// <param name="inputB">Another input channel.</param>
        /// <param name="output">The output result.</param>
        /// <param name="method">The zip method</param>
        /// <typeparam name="Ta">One data type parameter.</typeparam>
        /// <typeparam name="Tb">Another data type parameter.</typeparam>
        /// <typeparam name="TOut">The result type parameter.</typeparam>
        public static Task ZipAsync <Ta, Tb, TOut>(IReadChannel <Ta> inputA, IReadChannel <Tb> inputB, IWriteChannel <TOut> output, Func <Ta, Tb, Task <TOut> > method)
        {
            if (inputA == null)
            {
                throw new ArgumentNullException(nameof(inputA));
            }
            if (inputB == null)
            {
                throw new ArgumentNullException(nameof(inputB));
            }
            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }
            if (method == null)
            {
                throw new ArgumentNullException(nameof(method));
            }

            return(AutomationExtensions.RunTask(
                       new
            {
                InputA = inputA,
                InputB = inputB,
                Output = output
            },
                       async self =>
            {
                while (true)
                {
                    var readA = self.InputA.ReadAsync();
                    var readB = self.InputB.ReadAsync();
                    var vA = await readA;
                    var vB = await readB;
                    await output.WriteAsync(await method(vA, vB));
                }
            }
                       ));
        }
Beispiel #7
0
        /// <summary>
        /// Sends all values from the enumerable to a channel
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="data">The data to send.</param>
        /// <param name="target">The channel to send the data to.</param>
        /// <typeparam name="T">The data type parameter.</typeparam>
        public static Task EmitAsync <T>(IEnumerable <T> data, IWriteChannel <T> target)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }
            if (target == null)
            {
                throw new ArgumentNullException(nameof(target));
            }

            return(AutomationExtensions.RunTask(
                       new { Target = target },
                       async self =>
            {
                foreach (var n in data)
                {
                    await target.WriteAsync(n);
                }
            }
                       ));
        }
Beispiel #8
0
        /// <summary>
        /// Splits all values from a channel into two channels
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="input">The channel to read from.</param>
        /// <param name="outputA">One output channel.</param>
        /// <param name="outputB">Another output channel.</param>
        /// <param name="method">The method that performs the split.</param>
        /// <typeparam name="TIn">The input data type parameter.</typeparam>
        /// <typeparam name="Ta">One output data type parameter.</typeparam>
        /// <typeparam name="Tb">Another output data type parameter.</typeparam>
        public static Task SplitAsync <TIn, Ta, Tb>(IReadChannel <TIn> input, IWriteChannel <Ta> outputA, IWriteChannel <Tb> outputB, Func <TIn, Task <Tuple <Ta, Tb> > > method)
        {
            if (input == null)
            {
                throw new ArgumentNullException(nameof(input));
            }
            if (outputA == null)
            {
                throw new ArgumentNullException(nameof(outputA));
            }
            if (outputB == null)
            {
                throw new ArgumentNullException(nameof(outputB));
            }
            if (method == null)
            {
                throw new ArgumentNullException(nameof(method));
            }

            return(AutomationExtensions.RunTask(
                       new
            {
                Input = input,
                OutputA = outputA,
                OutputB = outputB
            },
                       async self =>
            {
                while (true)
                {
                    var n = await method(await self.Input.ReadAsync());
                    var writeA = self.OutputA.WriteAsync(n.Item1);
                    await self.OutputB.WriteAsync(n.Item2);
                    await writeA;
                }
            }
                       ));
        }
Beispiel #9
0
        /// <summary>
        /// Helper method that sets up a simple process that performs the same task for each received request,
        /// optionally performing the handlers in parallel
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="channel">The channel to handle messages for.</param>
        /// <param name="handler">The method to invoke for each message.</param>
        /// <param name="maxparallel">The maximum number of parallel handlers</param>
        /// <typeparam name="T">The channel data type parameter.</typeparam>
        public static Task CollectAsync <T>(IReadChannel <T> channel, Func <T, Task> handler, int maxparallel = 1)
        {
            if (channel == null)
            {
                throw new ArgumentNullException(nameof(channel));
            }
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }

            return(AutomationExtensions.RunTask(
                       new { Source = channel },
                       async self =>
            {
                // List of pending task; task zero is the reader
                var pending = new List <Task>()
                {
                    self.Source.ReadAsync()
                };

                while (true)
                {
                    // Wait for something to happen
                    var t = await Task.WhenAny(pending);

                    // If the reader completed
                    if (t == pending[0])
                    {
                        // Did it work?
                        if (t.IsCompleted && !(t.IsCanceled || t.IsFaulted))
                        {
                            if (pending.Count > maxparallel)
                            {
                                pending[0] = null;
                            }
                            else
                            {
                                pending[0] = Task.Run(() => self.Source.ReadAsync());
                            }

                            // Unwrap the data
                            var data = await(Task <T>) t;

                            // Add the handler process to the queue
                            pending.Add(Task.Run(() => handler(data)));
                        }
                        // Otherwise we are probably being terminated
                        else
                        {
                            await Task.WhenAll(pending.Skip(1));

                            // Re-throw the error
                            await t;
                        }
                    }

                    // Non-reader completed, move on
                    else
                    {
                        pending.Remove(t);
                        // We have space, so accept a new read, if required

                        if (pending[0] == null)
                        {
                            pending[0] = self.Source.ReadAsync();
                        }
                    }
                }
            }));
        }
Beispiel #10
0
        /// <summary>
        /// Sets up a simple process that forwards the inputs to the output
        /// </summary>
        /// <returns>The gather.</returns>
        /// <param name="input">Input.</param>
        /// <param name="output">Output.</param>
        /// <param name="policy">Policy.</param>
        /// <typeparam name="T">The 1st type parameter.</typeparam>
        public static Task GatherAsync <T>(IReadChannel <T>[] input, IWriteChannel <T> output, ScatterGatherPolicy policy = ScatterGatherPolicy.Any)
        {
            if (input == null || input.Any(x => x == null))
            {
                throw new ArgumentNullException(nameof(input));
            }
            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }

            return(AutomationExtensions.RunTask(
                       new { Input = input, Output = output },
                       async self =>
            {
                var lst = input.ToList();
                while (true)
                {
                    if (policy == ScatterGatherPolicy.Any)
                    {
                        try
                        {
                            await self.Output.WriteAsync((await lst.ReadFromAnyAsync()).Value);
                        }
                        catch (Exception ex)
                        {
                            if (ex.IsRetiredException())
                            {
                                var any = false;
                                for (var i = lst.Count - 1; i >= 0; i--)
                                {
                                    if (any |= await lst[i].IsRetiredAsync)
                                    {
                                        lst.RemoveAt(i);
                                    }
                                }

                                if (lst.Count > 0 && any)
                                {
                                    continue;
                                }
                            }

                            throw;
                        }
                    }
                    else
                    {
                        for (var i = 0; i < lst.Count; i++)
                        {
                            try
                            {
                                await self.Output.WriteAsync(await lst[i].ReadAsync());
                            }
                            catch (Exception ex)
                            {
                                if (ex.IsRetiredException() && lst.Count > 1)
                                {
                                    lst.RemoveAt(i);
                                    i--;
                                    continue;
                                }
                                throw;
                            }
                        }
                    }
                }
            }
                       ));
        }
Beispiel #11
0
 /// <summary>
 /// <summary>
 /// Releases all resource used by the <see cref="CoCoL.ProcessHelper"/> object.
 /// </summary>
 /// <remarks>Call <see cref="Dispose()"/> when you are finished using the <see cref="CoCoL.ProcessHelper"/>. The
 /// <see cref="Dispose()"/> method leaves the <see cref="CoCoL.ProcessHelper"/> in an unusable state. After calling
 /// <see cref="Dispose()"/>, you must release all references to the <see cref="CoCoL.ProcessHelper"/> so the garbage
 /// collector can reclaim the memory that the <see cref="CoCoL.ProcessHelper"/> was occupying.</remarks>
 /// </summary>
 /// <param name="disposing">If set to <c>true</c> disposing.</param>
 public virtual void Dispose(bool disposing)
 {
     AutomationExtensions.RetireAllChannels(this);
 }
Beispiel #12
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CoCoL.ProcessHelper"/> class.
 /// </summary>
 protected ProcessHelper()
 {
     AutomationExtensions.AutoWireChannelsDirect(this);
 }