public void UnlockCompetition(CancellationToken cancellationToken)
        {
            CancellationTokenRegistration registration = cancellationToken.Register(
                state =>
            {
                ExclusiveCompletionSourceGroup <T> group = state as ExclusiveCompletionSourceGroup <T>;

                /// There are 2 cases here.
                ///
                /// #1: The token is canceled before <see cref="UnlockCompetition(CancellationToken)"/> is called, but after the token is validated higher up the stack.
                /// Is this is the case, the cancellation callbak will be called synchronously while <see cref="_completedSource"/> is still set to <see cref="State.Locked"/>.
                /// So the competition will never progress to <see cref="State.Unlocked"/> and we have to check for this explicitly.
                ///
                /// #2: We're canceled after the competition has been unlocked.
                /// If this is the case, we have a simple race against the awaiters to progress from <see cref="State.Unlocked"/> to <see cref="State.Canceled"/>.
                if (group.TryTransitionToCanceledIfStateIs(State.Locked) || group.TryTransitionToCanceledIfStateIs(State.Unlocked))
                {
                    group._realCompetionSource.SetCanceled();
                }
            },
                this,
                useSynchronizationContext: false);

            // We can't do volatile reads/writes on a custom value type field, so we have to wrap the registration into a holder instance.
            // But there's no point in allocating the wrapper if the token can never be canceled.
            if (cancellationToken.CanBeCanceled)
            {
                Volatile.Write(ref _cancellationRegistrationHolder, new CancellationRegistrationHolder(registration));
            }

            // If the cancellation was processed synchronously, the state will already be set to Canceled and we must *NOT* unlock the competition.
            Interlocked.CompareExchange(ref _completedSource, State.Unlocked, State.Locked);
        }
Example #2
0
        /// <summary>
        /// Removes and returns an item from one of the specified collections in an asynchronous manner.
        /// </summary>
        public static Task <AnyResult <TakeResult <T> > > TakeFromAnyAsync(AsyncCollection <T>[] collections, CancellationToken cancellationToken)
        {
            if (collections == null)
            {
                throw new ArgumentNullException(nameof(collections));
            }

            if (collections.Length <= 0 || collections.Length > TakeFromAnyMaxCollections)
            {
                throw new ArgumentException(
                          $"The collection array can't contain less than 1 or more than {TakeFromAnyMaxCollections} collections.", nameof(collections));
            }

            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCanceled <AnyResult <TakeResult <T> > >(cancellationToken));
            }

            var exclusiveSources = new ExclusiveCompletionSourceGroup <TakeResult <T> >();

            //	Fast route: we attempt to take from the top-priority queues that have any items.
            //	If the fast route succeeds, we avoid allocating and queueing a bunch of awaiters.
            for (var i = 0; i < collections.Length; i++)
            {
                if (collections[i].Count > 0)
                {
                    var result = TryTakeFast(exclusiveSources, collections[i], i);
                    if (result.HasValue)
                    {
                        return(Task.FromResult(result.Value));
                    }
                }
            }

            //	No luck during the fast route; just queue the rest of awaiters.
            for (var i = 0; i < collections.Length; i++)
            {
                var result = TryTakeFast(exclusiveSources, collections[i], i);
                if (result.HasValue)
                {
                    return(Task.FromResult(result.Value));
                }
            }

            //	None of the collections had any items. The order doesn't matter anymore, it's time to start the competition.
            exclusiveSources.UnlockCompetition(cancellationToken);
            return(exclusiveSources.Task);
        }
Example #3
0
        private static AnyResult <TakeResult <T> >?TryTakeFast(ExclusiveCompletionSourceGroup <TakeResult <T> > exclusiveSources, AsyncCollection <T> collection, int index)
        {
            //	This can happen if the awaiter has already been created during the fast route.
            if (exclusiveSources.IsAwaiterCreated(index))
            {
                return(null);
            }

            var collectionTask = collection.TakeAsync(exclusiveSources.CreateAwaiterFactory(index));

            //	One of the collections already had an item and returned it directly
            if (collectionTask != null && collectionTask.IsCompleted)
            {
                exclusiveSources.MarkAsResolved();
                return(new AnyResult <TakeResult <T> >(collectionTask.Result, index));
            }
            return(null);
        }
 public Factory(ExclusiveCompletionSourceGroup <T> group, int index)
 {
     _group = group;
     _index = index;
 }
 public ExclusiveCompletionSource(ExclusiveCompletionSourceGroup <T> group, int id)
 {
     _group = group;
     _id    = id;
 }