internal bool TryJoinComputation()

in src/Microsoft.VisualStudio.Threading/CancellableJoinComputation.cs [147:254]


        internal bool TryJoinComputation(bool isInitialTask, [NotNullWhen(true)] out Task? task, CancellationToken cancellationToken)
        {
            if (!this.isCancellationAllowed)
            {
                task = this.JoinNotCancellableTaskAsync(isInitialTask, cancellationToken);
                return true;
            }

            if (cancellationToken.IsCancellationRequested)
            {
                if (isInitialTask)
                {
                    // It is a corner case the cancellation token is triggered right after the first task starts. It may need cancel the inner task.
                    CancellationTokenSource? cancellationTokenSource = null;
                    lock (this.syncObject)
                    {
                        if (this.isCancellationAllowed && this.outstandingWaitingCount == 0 && this.combinedCancellationTokenSource is not null)
                        {
                            this.isCancellationRequested = true;
                            cancellationTokenSource = this.combinedCancellationTokenSource;
                            this.combinedCancellationTokenSource = null;
                        }
                    }

                    if (cancellationTokenSource is not null)
                    {
                        cancellationTokenSource.Cancel();
                        cancellationTokenSource.Dispose();
                    }

                    task = this.InnerTask;
                    return true;
                }
                else
                {
                    task = Task.FromCanceled(cancellationToken);
                    return true;
                }
            }

            // if the inner task is joined by a new uncancellable task, we will abandone the cancellation token source because we will never use it anymore.
            // we do it outside of our lock.
            CancellationTokenSource? combinedCancellationTokenSourceToDispose = null;

            try
            {
                lock (this.syncObject)
                {
                    if (this.isCancellationRequested)
                    {
                        // If the earlier computation is aborted, we cannot join it anymore.
                        task = null;
                        return false;
                    }

                    if (this.InnerTask.IsCompleted)
                    {
                        task = this.InnerTask;
                        return true;
                    }

                    if (!cancellationToken.CanBeCanceled)
                    {
                        // A single joined client which doesn't allow cancellation would turn the entire computation not cancellable.
                        combinedCancellationTokenSourceToDispose = this.combinedCancellationTokenSource;
                        this.combinedCancellationTokenSource = null;

                        this.isCancellationAllowed = false;

                        task = this.JoinNotCancellableTaskAsync(isInitialTask, CancellationToken.None);
                    }
                    else if (!this.isCancellationAllowed)
                    {
                        task = this.JoinNotCancellableTaskAsync(isInitialTask, cancellationToken);
                    }
                    else
                    {
                        Assumes.NotNull(this.joinedWaitingList);

                        WaitingCancellationStatus status;

                        // we need increase the outstanding count before creating WiatingCancellationStatus.
                        // Under a rare race condition the cancellation token can be trigger with this time frame, and lead OnWaitingTaskCancelled to be called recursively
                        // within this lock. It would be critical to make sure the outstandingWaitingCount to increase before decreasing there.
                        this.outstandingWaitingCount++;
                        try
                        {
                            status = new WaitingCancellationStatus(this, cancellationToken);
                        }
                        catch
                        {
                            this.outstandingWaitingCount--;
                            throw;
                        }

                        this.joinedWaitingList.Add(status);

                        task = status.Task;
                    }
                }
            }
            finally
            {
                combinedCancellationTokenSourceToDispose?.Dispose();
            }

            return true;
        }