private async Task PromoteTaskToActiveAsync(BackgroundRestoreOperation restoreOperation, CancellationToken token)
        {
            var pendingTask = restoreOperation.Task;

            int attempt = 0;

            for (var retry = true;
                 retry && !token.IsCancellationRequested && attempt != PromoteAttemptsLimit;
                 attempt++)
            {
                // Grab local copy of active task
                var activeTask = _activeRestoreTask;

                // Await for the completion of the active *unbound* task
                var cancelTcs = new TaskCompletionSource <bool>();
                using (var ctr = token.Register(() => cancelTcs.TrySetCanceled()))
                {
                    await Task.WhenAny(activeTask, cancelTcs.Task);
                }

                // Try replacing active task with the new one.
                // Retry from the beginning if the active task has changed.
                retry = Interlocked.CompareExchange(
                    ref _activeRestoreTask, pendingTask, activeTask) != activeTask;
            }

            if (attempt == PromoteAttemptsLimit)
            {
                throw new InvalidOperationException("Failed promoting pending task.");
            }
        }
Пример #2
0
        public async Task <bool> RestoreAsync(SolutionRestoreRequest request, CancellationToken token)
        {
            // Signal that restore is running
            _isCompleteEvent.Reset();

            try
            {
                using (_joinableCollection.Join())
                {
                    await StartInitializationAsync();

                    using (var restoreOperation = new BackgroundRestoreOperation())
                    {
                        await PromoteTaskToActiveAsync(restoreOperation, token);

                        var result = await ProcessRestoreRequestAsync(restoreOperation, request, token);

                        return(result);
                    }
                }
            }
            finally
            {
                // Signal that restore has been completed.
                _isCompleteEvent.Set();
            }
        }
        private async Task <bool> ProcessRestoreRequestAsync(
            BackgroundRestoreOperation restoreOperation,
            SolutionRestoreRequest request,
            CancellationToken token)
        {
            // Start the restore job in a separate task on a background thread
            // it will switch into main thread when necessary.
            var joinableTask = _joinableFactory.RunAsync(
                () => StartRestoreJobAsync(request, token));

            var continuation = joinableTask
                               .Task
                               .ContinueWith(t => restoreOperation.ContinuationAction(t));

            return(await joinableTask);
        }
Пример #4
0
        public async Task <bool> RestoreAsync(SolutionRestoreRequest request, CancellationToken token)
        {
            using (_joinableCollection.Join())
            {
                // Initialize if not already done.
                await InitializeAsync();

                using (var restoreOperation = new BackgroundRestoreOperation())
                {
                    await PromoteTaskToActiveAsync(restoreOperation, token);

                    var result = await ProcessRestoreRequestAsync(restoreOperation, request, token);

                    return(result);
                }
            }
        }
        private void Reset(bool isDisposing = false)
        {
            // Make sure worker restore operation is cancelled
            _workerCts?.Cancel();

            if (_backgroundJobRunner?.IsValueCreated == true)
            {
                // Await completion of the background work
                _joinableFactory.Run(
                    async() =>
                {
                    // Do not block VS forever
                    // After the specified delay the task will disjoin.
                    await _backgroundJobRunner.GetValueAsync().WithTimeout(TimeSpan.FromSeconds(60));
                },
                    JoinableTaskCreationOptions.LongRunning);
            }

            _pendingRestore?.Dispose();
            _workerCts?.Dispose();

            if (_pendingRequests?.IsValueCreated == true)
            {
                _pendingRequests.Value.Dispose();
            }

            if (!isDisposing)
            {
                _solutionLoadedEvent.Reset();

                _workerCts = new CancellationTokenSource();

                _backgroundJobRunner = new AsyncLazy <bool>(
                    () => StartBackgroundJobRunnerAsync(_workerCts.Token),
                    _joinableFactory);

                _pendingRequests = new Lazy <BlockingCollection <SolutionRestoreRequest> >(
                    () => new BlockingCollection <SolutionRestoreRequest>(RequestQueueLimit));

                _pendingRestore    = new BackgroundRestoreOperation();
                _activeRestoreTask = Task.FromResult(true);
                _restoreJobContext = new SolutionRestoreJobContext();
            }
        }
        public bool Restore(SolutionRestoreRequest request)
        {
            return(_joinableFactory.Run(
                       async() =>
            {
                using (_joinableCollection.Join())
                {
                    // Initialize if not already done.
                    await InitializeAsync();

                    using (var restoreOperation = new BackgroundRestoreOperation())
                    {
                        await PromoteTaskToActiveAsync(restoreOperation, _workerCts.Token);

                        var result = await ProcessRestoreRequestAsync(restoreOperation, request, _workerCts.Token);

                        return result;
                    }
                }
            },
                       JoinableTaskCreationOptions.LongRunning));
        }
Пример #7
0
        private async Task <bool> ProcessRestoreRequestAsync(
            BackgroundRestoreOperation restoreOperation,
            SolutionRestoreRequest request,
            CancellationToken token)
        {
            // if the request is implicit & this is the first restore, assume we are restoring due to a solution load.
            var isSolutionLoadRestore = _isFirstRestore &&
                                        request.RestoreSource == RestoreOperationSource.Implicit;

            _isFirstRestore = false;

            // Start the restore job in a separate task on a background thread
            // it will switch into main thread when necessary.
            var joinableTask = JoinableTaskFactory.RunAsync(
                () => StartRestoreJobAsync(request, isSolutionLoadRestore, token));

            var continuation = joinableTask
                               .Task
                               .ContinueWith(t => restoreOperation.ContinuationAction(t, JoinableTaskFactory));

            return(await joinableTask);
        }
Пример #8
0
        public async Task <bool> ScheduleRestoreAsync(
            SolutionRestoreRequest request, CancellationToken token)
        {
            if (token.IsCancellationRequested)
            {
                return(false);
            }

            // Reset to signal that a restore is in progress.
            // This sets IsRunning to true
            _isCompleteEvent.Reset();

            try
            {
                await StartInitializationAsync();

                BackgroundRestoreOperation pendingRestore = _pendingRestore;

                // lock _pendingRequests to figure out if we need to start a new background job for restore
                // or if there is already one running which will also take care of current request.
                lock (_lockPendingRequestsObj)
                {
                    var shouldStartNewBGJobRunner = true;

                    // check if there are already pending restore request or active restore task
                    // then don't initiate a new background job runner.
                    if (_pendingRequests.Value.Count > 0 || IsBusy)
                    {
                        shouldStartNewBGJobRunner = false;
                    }
                    else if (_lockService.Value.IsLockHeld && _lockService.Value.LockCount > 0)
                    {
                        // when restore is not running but NuGet lock is still held for the current async operation,
                        // then it means other NuGet operation like Install or Update are in progress which will
                        // take care of running restore for appropriate projects so skipping auto restore in that case.
                        return(true);
                    }

                    AsyncLazy <bool> backgroundJobRunner = _backgroundJobRunner;

                    if (backgroundJobRunner == null)
                    {
                        shouldStartNewBGJobRunner = true;
                    }
                    else if (backgroundJobRunner.IsValueCreated && backgroundJobRunner.IsValueFactoryCompleted)
                    {
                        Task <bool> valueTask = backgroundJobRunner.GetValueAsync();

                        if (valueTask.IsFaulted || valueTask.IsCanceled)
                        {
                            shouldStartNewBGJobRunner = true;
                        }
                    }

                    // on-board request onto pending restore operation
                    _pendingRequests.Value.TryAdd(request);

                    // When there is no current background restore job running, then start a new one.
                    // Otherwise, the current request will await the existing job to be completed.
                    if (shouldStartNewBGJobRunner)
                    {
                        _backgroundJobRunner = new AsyncLazy <bool>(
                            () => StartBackgroundJobRunnerAsync(_workerCts.Token),
                            JoinableTaskFactory);
                    }
                }

                try
                {
                    using (_joinableCollection.Join())
                    {
                        // Await completion of the requested restore operation or
                        // completion of the current job runner.
                        // The caller will be unblocked immediately upon
                        // cancellation request via provided token.
                        return(await await Task
                               .WhenAny(
                                   pendingRestore.Task,
                                   _backgroundJobRunner.GetValueAsync())
                               .WithCancellation(token));
                    }
                }
                catch (OperationCanceledException) when(token.IsCancellationRequested)
                {
                    return(false);
                }
                catch (Exception e)
                {
                    Logger.LogError(e.ToString());
                    return(false);
                }
            }
            finally
            {
                // Signal that all pending operations are complete.
                _isCompleteEvent.Set();
            }
        }