Example #1
0
        public static Task <Tuple <K, V> > CompleteAny <K, V>(this ImmutableList <Tuple <K, CancellableOperationStarter <V> > > operationStarters, CancellationToken ctoken)
        {
            object syncRoot = new object();
            TaskCompletionSource <Tuple <K, V> > tcs = new TaskCompletionSource <Tuple <K, V> >();

            ImmutableHashSet <int> waits    = ImmutableHashSet <int> .Empty;
            ImmutableHashSet <int> canceled = ImmutableHashSet <int> .Empty;

            CancellableOperation <V>[] operations = new CancellableOperation <V> [operationStarters.Count];
            int?firstIndex  = null;
            V   firstResult = default(V);

            CancellationTokenRegistration?ctr = null;

            Action <CancellationTokenRegistration> setRegistration = delegate(CancellationTokenRegistration value) {
                lock (syncRoot) {
                    if (firstIndex.HasValue && waits.IsEmpty)
                    {
                        value.PostDispose();
                    }
                    else
                    {
                        ctr = value;
                    }
                }
            };

            Action deliverFinalResult = delegate {
                #region

                List <Exception> exc = new List <Exception>();

                foreach (int i in Enumerable.Range(0, operations.Length))
                {
                    if (operations[i] == null)
                    {
                        continue;
                    }

                    System.Diagnostics.Debug.Assert(operations[i].HasEnded);

                    if (operations[i].Task.IsFaulted)
                    {
                        Exception eTask = operations[i].Task.Exception;
                        if (!(eTask is OperationCanceledException) || !(canceled.Contains(i)))
                        {
                            exc.Add(eTask);
                        }
                    }
                    else if (operations[i].Task.IsCanceled)
                    {
                        if (!canceled.Contains(i))
                        {
                            exc.Add(new OperationCanceledException());
                        }
                    }
                }

                System.Diagnostics.Debug.Assert(firstIndex.HasValue);
                int fi = firstIndex.Value;

                if (exc.Count == 0)
                {
                    if (fi >= 0)
                    {
                        tcs.PostResult(new Tuple <K, V>(operationStarters[fi].Item1, firstResult));
                    }
                    else
                    {
                        tcs.PostException(new OperationCanceledException(ctoken));
                    }
                }
                else if (exc.Count == 1)
                {
                    tcs.PostException(exc[0]);
                }
                else
                {
                    tcs.PostException(new AggregateException(exc));
                }

                if (ctr.HasValue)
                {
                    ctr.Value.PostDispose();
                }

                #endregion
            };

            Action unwind = delegate {
                int j = operationStarters.Count;
                while (j > 0)
                {
                    --j;
                    if ((!firstIndex.HasValue) || j != firstIndex.Value)
                    {
                        if (operations[j] != null)
                        {
                            operations[j].Cancel();
                            canceled = canceled.Add(j);
                        }
                    }
                }
            };

            Action handleCancellation = delegate {
                lock (syncRoot) {
                    if (!firstIndex.HasValue)
                    {
                        firstIndex = -1;
                        unwind();
                    }
                }
            };

            Action <int> handleItemCompletion = delegate(int i) {
                lock (syncRoot) {
                    System.Diagnostics.Debug.Assert(waits.Contains(i));
                    waits = waits.Remove(i);

                    if (firstIndex.HasValue)
                    {
                        if (operations[i].Task.Status == TaskStatus.RanToCompletion)
                        {
                            operations[i].Task.Result.Cancel();
                            canceled = canceled.Add(i);
                        }
                    }
                    else
                    {
                        firstIndex = i;

                        if (operations[i].Task.Status == TaskStatus.RanToCompletion)
                        {
                            firstResult = operations[i].Task.Result.Complete();
                        }

                        unwind();
                    }

                    if (waits.IsEmpty)
                    {
                        deliverFinalResult();
                    }
                }
            };

            lock (syncRoot) {
                bool shouldUnwind = false;

                ctoken.PostRegistration(setRegistration, handleCancellation);

                foreach (int i in Enumerable.Range(0, operationStarters.Count))
                {
                    CancellableOperation <V> op = operationStarters[i].Item2(ctoken);
                    operations[i] = op;
                    if (op.HasEnded)
                    {
                        shouldUnwind = true;
                        firstIndex   = i;
                        if (op.Task.IsCompleted)
                        {
                            firstResult = op.Task.Result.Complete();
                        }

                        break;
                    }
                    else
                    {
                        waits = waits.Add(i);

                        //continuations[i] =
                        op.Task.PostContinueWith
                        (
                            taskUnused => { handleItemCompletion(i); }
                        );
                    }
                }

                if (shouldUnwind)
                {
                    if (!waits.IsEmpty)
                    {
                        unwind();
                    }
                    else
                    {
                        deliverFinalResult();
                    }
                }
            }

            return(tcs.Task);
        }