/// <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; } } } )); }
/// <summary> /// Reads from any channel /// </summary> /// <param name="timeout">The maximum time to wait for a result.</param> public Task <MultisetResult <T> > ReadFromAnyAsync(TimeSpan timeout) { if (m_priority == MultiChannelPriority.Fair) { return(MultiChannelAccess.ReadFromAnyAsync( m_sortedChannels.NotifyUsed, m_sortedChannels.Channels, timeout, MultiChannelPriority.First )); } else { return(MultiChannelAccess.ReadFromAnyAsync( null, m_channels, timeout, m_priority )); } }
/// <summary> /// Writes to any of the channels. /// </summary> /// <param name="value">The value to write into the channel.</param> /// <param name="timeout">The maximum time to wait for any channel to become ready.</param> public Task <IWriteChannel <T> > WriteToAnyAsync(T value, TimeSpan timeout) { if (m_priority == MultiChannelPriority.Fair) { return(MultiChannelAccess.WriteToAnyAsync( m_sortedChannels.NotifyUsed, value, m_sortedChannels.Channels, timeout, MultiChannelPriority.First )); } else { return(MultiChannelAccess.WriteToAnyAsync( null, value, m_channels, timeout, m_priority )); } }
/// <summary> /// Reads or writes any of the specified requests /// </summary> /// <param name="callback">The method to call when the read completes, or null.</param> /// <param name="requests">The list of requests.</param> /// <param name="timeout">The maximum time to wait for a value to read.</param> /// <param name="priority">The priority used to select a channel, if multiple channels have a value that can be read.</param> public static Task <IMultisetRequestUntyped> ReadOrWriteAnyAsync(Action <object> callback, IEnumerable <IMultisetRequestUntyped> requests, TimeSpan timeout, MultiChannelPriority priority) { var tcs = new TaskCompletionSource <IMultisetRequestUntyped>(); // We only accept the first offer var offer = new SingleOffer <IMultisetRequestUntyped>(tcs, timeout == Timeout.Infinite ? Timeout.InfiniteDateTime : DateTime.Now + timeout); offer.SetCommitCallback(callback); switch (priority) { case MultiChannelPriority.Fair: throw new Exception(string.Format("Construct a {0} or {1} object to use fair multichannel operations", typeof(MultiChannelSetRead <>).Name, typeof(MultiChannelSetWrite <>).Name)); case MultiChannelPriority.Random: requests = MultiChannelAccess.Shuffle(requests); break; default: // Use the order the input has break; } // Keep a map of awaitable items var tasks = new Dictionary <Task, IMultisetRequestUntyped>(); // Then we register the intent to read from a channel in order foreach (var c in requests) { // Timeout is handled by offer instance if (c.IsRead) { tasks[c.Channel.ReadAsync(Timeout.Infinite, offer)] = c; } else { tasks[c.Channel.WriteAsync(c.Value, Timeout.Infinite, offer)] = c; } // Fast exit to avoid littering the channels if we are done if (offer.IsTaken) { break; } } offer.ProbePhaseComplete(); if (tasks.Count == 0) { tcs.TrySetException(new InvalidOperationException("List of channels was empty")); return(tcs.Task); } tasks.Keys.WhenAnyNonCancelled().ContinueWith(item => Task.Run(() => { if (item.IsCanceled) { tcs.TrySetCanceled(); return; } else if (item.IsFaulted) { tcs.TrySetException(item.Exception); return; } if (offer.AtomicIsFirst()) { var n = item.Result; // Figure out which item was found if (n.IsCanceled) { tcs.SetCanceled(); } else if (n.IsFaulted) { // Unwrap aggregate exceptions if (n.Exception is AggregateException && (n.Exception as AggregateException).Flatten().InnerExceptions.Count == 1) { tcs.SetException(n.Exception.InnerException); } else { tcs.SetException(n.Exception); } } else { var orig = tasks[n]; if (orig.IsRead) { orig.Value = ((Task <object>)n).Result; tcs.SetResult(orig); } else { orig.Value = null; tcs.SetResult(orig); } } } })); return(tcs.Task); }