Example #1
0
        /// <summary>
        /// Reads from any of the specified channels
        /// </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>
        /// <param name="cancelToken">The cancellation token</param>
        /// <typeparam name="T">The channel data type.</typeparam>
        private static Task <MultisetRequest <T> > ReadOrWriteAnyAsync <T>(Action <object> callback, IEnumerable <MultisetRequest <T> > requests, TimeSpan timeout, MultiChannelPriority priority, CancellationToken cancelToken = default(CancellationToken))
        {
            // This method could also use the untyped version,
            // but using the type version is faster as there are no reflection
            // or boxing/typecasting required

            var tcs = new TaskCompletionSource <MultisetRequest <T> >();

            // We only accept the first offer
            var offer = new SingleOffer <MultisetRequest <T> >(tcs, timeout == Timeout.Infinite ? Timeout.InfiniteDateTime : DateTime.Now + timeout, cancelToken);

            offer.SetCommitCallback(callback);

            switch (priority)
            {
            case MultiChannelPriority.Fair:
                throw new InvalidOperationException(string.Format("Construct a {0} or {1} object to use fair multichannel operations", typeof(MultiChannelSetRead <>).Name, typeof(MultiChannelSetWrite <>).Name));

            case MultiChannelPriority.Random:
                requests = Shuffle(requests);
                break;

            default:
                // Use the order the input has
                break;
            }

            // Keep a map of awaitable items
            // and register the intent to read from a channel in order
            var tasks = new Dictionary <Task, MultisetRequest <T> >();

            foreach (var c in requests)
            {
                // Timeout is handled by offer instance
                if (c.IsRead)
                {
                    tasks[c.ReadChannel.ReadAsync(offer)] = c;
                }
                else
                {
                    tasks[c.WriteChannel.WriteAsync(c.Value, 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;
                }

                var n = item.Result;

                if (offer.AtomicIsFirst())
                {
                    // 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)
                        {
                            tcs.SetResult(new MultisetRequest <T>(((Task <T>)n).Result, orig.ReadChannel, null, true));
                        }
                        else
                        {
                            tcs.SetResult(new MultisetRequest <T>(default(T), null, orig.WriteChannel, false));
                        }
                    }
                }
            }));

            return(tcs.Task);
        }
Example #2
0
        /// <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);
        }