public async Task <bool> NominateProjectAsync(string projectUniqueName, CancellationToken token) { Assumes.NotNullOrEmpty(projectUniqueName); if (!_projectSystemCache.TryGetProjectNames(projectUniqueName, out ProjectNames projectNames)) { IVsSolution2 vsSolution2 = await _vsSolution2.GetValueAsync(token); projectNames = await ProjectNames.FromIVsSolution2(projectUniqueName, vsSolution2, token); } var dgSpec = new DependencyGraphSpec(); var packageSpec = new PackageSpec() { Name = projectUniqueName }; dgSpec.AddProject(packageSpec); dgSpec.AddRestore(packageSpec.Name); _projectSystemCache.AddProjectRestoreInfo(projectNames, dgSpec, new List <IAssetsLogMessage>()); // returned task completes when scheduled restore operation completes. var restoreTask = _restoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.OnUpdate(), token); return(await restoreTask); }
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(); } }
/// <summary> /// This is where the nominate calls for the IVs1 and IVS3 APIs combine. The reason for this method is to avoid duplication and potential issues /// The issue with this method is that it has some weird custom logging to ensure backward compatibility. It's on the implementer to ensure these calls are correct. /// <param name="projectUniqueName">projectUniqueName</param> /// <param name="projectRestoreInfo">projectRestoreInfo. Can be null</param> /// <param name="projectRestoreInfo2">proectRestoreInfo2. Can be null</param> /// <param name="token"></param> /// <remarks>Exactly one of projectRestoreInfos has to null.</remarks> /// <returns>The task that scheduled restore</returns> private Task <bool> NominateProjectAsync(string projectUniqueName, IVsProjectRestoreInfo projectRestoreInfo, IVsProjectRestoreInfo2 projectRestoreInfo2, CancellationToken token) { if (string.IsNullOrEmpty(projectUniqueName)) { throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(projectUniqueName)); } if (projectRestoreInfo == null && projectRestoreInfo2 == null) { throw new ArgumentNullException(nameof(projectRestoreInfo)); } if (projectRestoreInfo != null && projectRestoreInfo2 != null) { throw new ArgumentException($"Internal error: Both {nameof(projectRestoreInfo)} and {nameof(projectRestoreInfo2)} cannot have values. Please file an issue at NuGet/Home if you see this exception."); } if (projectRestoreInfo != null) { if (projectRestoreInfo.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } else { if (projectRestoreInfo2.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } try { _logger.LogInformation( $"The nominate API is called for '{projectUniqueName}'."); var projectNames = ProjectNames.FromFullProjectPath(projectUniqueName); var dgSpec = ToDependencyGraphSpec(projectNames, projectRestoreInfo, projectRestoreInfo2); _projectSystemCache.AddProjectRestoreInfo(projectNames, dgSpec); // returned task completes when scheduled restore operation completes. var restoreTask = _restoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.OnUpdate(), token); return(restoreTask); } catch (OperationCanceledException) { throw; } catch (Exception e) { _logger.LogError(e.ToString()); return(Task.FromResult(false)); } }
private async Task <bool> StartRestoreJobAsync( SolutionRestoreRequest request, CancellationToken token) { await TaskScheduler.Default; using (var jobCts = CancellationTokenSource.CreateLinkedTokenSource(token)) { return(await _lockService.Value.ExecuteNuGetOperationAsync(async() => { var componentModel = await _componentModel.GetValueAsync(jobCts.Token); using (var logger = componentModel.GetService <RestoreOperationLogger>()) { try { // Start logging await logger.StartAsync( request.RestoreSource, _errorListTableDataSource, _joinableFactory, jobCts); // Run restore var job = componentModel.GetService <ISolutionRestoreJob>(); return await job.ExecuteAsync(request, _restoreJobContext, logger, jobCts.Token); } finally { // Complete all logging await logger.StopAsync(); } } }, jobCts.Token)); } }
public async Task RestoreSolutionAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); await _initializer.InitializeAsync(cancellationToken); await SolutionRestoreWorker.Value.ScheduleRestoreAsync(SolutionRestoreRequest.ByUserCommand(ExplicitRestoreReason.RestoreSolutionPackages), cancellationToken); }
public async Task RestoreSolutionAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); await _initializer.InitializeAsync(cancellationToken); await SolutionRestoreWorker.Value.ScheduleRestoreAsync(SolutionRestoreRequest.ByMenu(), cancellationToken); }
private async Task <bool> RestoreProjectAsync(CancellationToken token) { await ShowProgressUIAsync(); OperationId = Guid.NewGuid(); return(await _solutionRestoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.ByMenu(), token)); }
private async Task <bool> RestoreProjectAsync(CancellationToken token) { await ShowProgressUIAsync(); OperationId = Guid.NewGuid(); return(await _solutionRestoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.ByUserCommand(ExplicitRestoreReason.MissingPackagesBanner), token)); }
/// <summary> /// This function is the callback used to execute the command when the menu item is clicked. /// See the constructor to see how the menu item is associated with this function using /// OleMenuCommandService service and MenuCommand class. /// </summary> /// <param name="sender">Event sender.</param> /// <param name="e">Event args.</param> private void OnRestorePackages(object sender, EventArgs args) { if (_restoreTask.IsCompleted) { _restoreTask = NuGetUIThreadHelper.JoinableTaskFactory .RunAsync(() => SolutionRestoreWorker.Value.ScheduleRestoreAsync( SolutionRestoreRequest.ByMenu(), CancellationToken.None)) .Task; } }
public Task <bool> NominateProjectAsync(string projectUniqueName, CancellationToken token) { Assumes.NotNullOrEmpty(projectUniqueName); // returned task completes when scheduled restore operation completes. var restoreTask = _restoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.OnUpdate(), token); return(restoreTask); }
public async Task Simple_WhenCancelled_Reports_Cancelled_Async() { var restoreMan = Mock.Of <IPackageRestoreManager>(); _globalProvider.AddService(typeof(IPackageRestoreManager), restoreMan); var slnMan = Mock.Of <IVsSolutionManager>(); _globalProvider.AddService(typeof(IVsSolutionManager), slnMan); ISourceRepositoryProvider sourceRepositoryProvider = TestSourceRepositoryUtility.CreateV3OnlySourceRepositoryProvider(); _globalProvider.AddService(typeof(ISourceRepositoryProvider), sourceRepositoryProvider); var restoreChecker = Mock.Of <ISolutionRestoreChecker>(); var eventsPublisher = Mock.Of <IRestoreEventsPublisher>(); var settings = Mock.Of <ISettings>(); var nuGetProgressReporter = Mock.Of <IVsNuGetProgressReporter>(); Mock.Get(settings) .Setup(x => x.GetSection("packageRestore")) .Returns(() => new VirtualSettingSection("packageRestore", new AddItem("automatic", bool.TrueString))); var consoleProvider = Mock.Of <IOutputConsoleProvider>(); var logger = new RestoreOperationLogger(new Lazy <IOutputConsoleProvider>(() => consoleProvider)); var job = new SolutionRestoreJob( asyncServiceProvider: AsyncServiceProvider.GlobalProvider, packageRestoreManager: restoreMan, solutionManager: slnMan, sourceRepositoryProvider: sourceRepositoryProvider, restoreEventsPublisher: eventsPublisher, settings: settings, solutionRestoreChecker: restoreChecker, nuGetProgressReporter: nuGetProgressReporter); var restoreRequest = new SolutionRestoreRequest( forceRestore: true, RestoreOperationSource.OnBuild); var restoreJobContext = new SolutionRestoreJobContext(); var cts = new CancellationTokenSource(); cts.Cancel(); await job.ExecuteAsync( request : restoreRequest, jobContext : restoreJobContext, logger : logger, trackingData : new Dictionary <string, object>(), token : cts.Token); Assert.Equal(NuGetOperationStatus.Cancelled, job.Status); }
/// <summary> /// Restore job entry point. Not re-entrant. /// </summary> public async Task <bool> ExecuteAsync( SolutionRestoreRequest request, SolutionRestoreJobContext jobContext, RestoreOperationLogger logger, CancellationToken token) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (jobContext == null) { throw new ArgumentNullException(nameof(jobContext)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } _logger = logger; // update instance attributes with the shared context values _dependencyGraphProjectCacheHash = jobContext.DependencyGraphProjectCacheHash; _nuGetProjectContext = jobContext.NuGetProjectContext; using (var ctr1 = token.Register(() => _status = NuGetOperationStatus.Cancelled)) { try { await RestoreAsync(request.ForceRestore, request.RestoreSource, token); } catch (OperationCanceledException) when(token.IsCancellationRequested) { } catch (Exception e) { // Log the exception to the console and activity log await _logger.LogExceptionAsync(e); } finally { // update shared context values with instance attributes jobContext.DependencyGraphProjectCacheHash = _dependencyGraphProjectCacheHash; } } return(_status == NuGetOperationStatus.NoOp || _status == NuGetOperationStatus.Succeeded); }
public Task <bool> NominateProjectAsync(string projectUniqueName, IVsProjectRestoreInfo projectRestoreInfo, CancellationToken token) { if (string.IsNullOrEmpty(projectUniqueName)) { throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(projectUniqueName)); } if (projectRestoreInfo == null) { throw new ArgumentNullException(nameof(projectRestoreInfo)); } if (projectRestoreInfo.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } try { _logger.LogInformation( $"The nominate API is called for '{projectUniqueName}'."); var projectNames = ProjectNames.FromFullProjectPath(projectUniqueName); var dgSpec = ToDependencyGraphSpec(projectNames, projectRestoreInfo); #if DEBUG DumpProjectRestoreInfo(projectUniqueName, dgSpec); #endif _projectSystemCache.AddProjectRestoreInfo(projectNames, dgSpec); // returned task completes when scheduled restore operation completes. var restoreTask = _restoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.OnUpdate(), token); return(restoreTask); } catch (Exception e) when(e is InvalidOperationException || e is ArgumentException || e is FormatException) { _logger.LogError(e.ToString()); return(Task.FromResult(false)); } catch (Exception e) { _logger.LogError(e.ToString()); throw; } }
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); }
public static RestoreTask Start(ISolutionRestoreWorker worker, bool forceRestore) { var cts = new CancellationTokenSource(); var task = worker .JoinableTaskFactory .RunAsync(() => worker.ScheduleRestoreAsync( SolutionRestoreRequest.OnBuild(forceRestore), cts.Token)) .Task; return(new RestoreTask { _cts = cts, _task = task }); }
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); } } }
/// <summary> /// Restore job entry point. Not re-entrant. /// </summary> public async Task <bool> ExecuteAsync( SolutionRestoreRequest request, SolutionRestoreJobContext jobContext, RestoreOperationLogger logger, bool isSolutionLoadRestore, CancellationToken token) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (jobContext == null) { throw new ArgumentNullException(nameof(jobContext)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } _logger = logger; // update instance attributes with the shared context values _nuGetProjectContext = jobContext.NuGetProjectContext; _nuGetProjectContext.OperationId = request.OperationId; _isSolutionLoadRestore = isSolutionLoadRestore; try { await RestoreAsync(request.ForceRestore, request.RestoreSource, token); } catch (OperationCanceledException) { } catch (Exception e) { // Log the exception to the console and activity log await _logger.LogExceptionAsync(e); } return(_status == NuGetOperationStatus.NoOp || _status == NuGetOperationStatus.Succeeded); }
public async Task <bool> ScheduleRestoreAsync( SolutionRestoreRequest request, CancellationToken token) { if (token.IsCancellationRequested) { return(false); } // Initialize if not already done. await InitializeAsync(); var pendingRestore = _pendingRestore; // on-board request onto pending restore operation _pendingRequests.Value.TryAdd(request); try { using (_joinableCollection.Join()) { // Await completion of the requested restore operation or // preliminary termination of the 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); } }
/// <summary> /// This function is the callback used to execute the command when the menu item is clicked. /// See the constructor to see how the menu item is associated with this function using /// OleMenuCommandService service and MenuCommand class. /// </summary> /// <param name="sender">Event sender.</param> /// <param name="e">Event args.</param> private void OnRestorePackages(object sender, EventArgs args) { if (!_isMEFInitialized) { NuGetUIThreadHelper.JoinableTaskFactory.Run(async() => { await InitializeMEFAsync(); }); _isMEFInitialized = true; } if (_restoreTask.IsCompleted) { _restoreTask = NuGetUIThreadHelper.JoinableTaskFactory .RunAsync(() => SolutionRestoreWorker.Value.ScheduleRestoreAsync( SolutionRestoreRequest.ByUserCommand(ExplicitRestoreReason.RestoreSolutionPackages), CancellationToken.None)) .Task; } }
private void BuildEvents_OnBuildBegin( EnvDTE.vsBuildScope scope, EnvDTE.vsBuildAction Action) { if (Action == EnvDTE.vsBuildAction.vsBuildActionClean) { // Clear the project.json restore cache on clean to ensure that the next build restores again SolutionRestoreWorker.CleanCache(); return; } if (!ShouldRestoreOnBuild) { return; } var forceRestore = Action == EnvDTE.vsBuildAction.vsBuildActionRebuildAll; // Execute SolutionRestoreWorker.Restore(SolutionRestoreRequest.OnBuild(forceRestore)); }
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); }
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)); }
public async Task <bool> RestoreAsync(uint buildAction, CancellationToken token) { // move to bg thread to continue with restore await TaskScheduler.Default; if ((buildAction & (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_CLEAN) != 0 && (buildAction & (uint)VSSOLNBUILDUPDATEFLAGS3.SBF_FLAGS_UPTODATE_CHECK) == 0) { // Clear the project.json restore cache on clean to ensure that the next build restores again await SolutionRestoreWorker.Value.CleanCacheAsync(); } else if ((buildAction & (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_BUILD) != 0 && (buildAction & (uint)VSSOLNBUILDUPDATEFLAGS3.SBF_FLAGS_UPTODATE_CHECK) == 0 && ShouldRestoreOnBuild) { // start a restore task var forceRestore = (buildAction & (uint)VSSOLNBUILDUPDATEFLAGS.SBF_OPERATION_FORCE_UPDATE) != 0; var restoreTask = SolutionRestoreWorker.Value.JoinableTaskFactory.RunAsync(() => SolutionRestoreWorker.Value.RestoreAsync( SolutionRestoreRequest.OnBuild(forceRestore), token)); // wait until restore is done which will block build without blocking UI thread return(await restoreTask); } return(true); }
/// <summary> /// This is where the nominate calls for the IVs1 and IVS3 APIs combine. The reason for this method is to avoid duplication and potential issues /// The issue with this method is that it has some weird custom logging to ensure backward compatibility. It's on the implementer to ensure these calls are correct. /// <param name="projectUniqueName">projectUniqueName</param> /// <param name="projectRestoreInfo">projectRestoreInfo. Can be null</param> /// <param name="projectRestoreInfo2">proectRestoreInfo2. Can be null</param> /// <param name="token"></param> /// <remarks>Exactly one of projectRestoreInfos has to null.</remarks> /// <returns>The task that scheduled restore</returns> private Task <bool> NominateProjectAsync(string projectUniqueName, IVsProjectRestoreInfo projectRestoreInfo, IVsProjectRestoreInfo2 projectRestoreInfo2, CancellationToken token) { if (string.IsNullOrEmpty(projectUniqueName)) { throw new ArgumentException(Resources.Argument_Cannot_Be_Null_Or_Empty, nameof(projectUniqueName)); } if (projectRestoreInfo == null && projectRestoreInfo2 == null) { throw new ArgumentNullException(nameof(projectRestoreInfo)); } if (projectRestoreInfo != null && projectRestoreInfo2 != null) { throw new ArgumentException($"Internal error: Both {nameof(projectRestoreInfo)} and {nameof(projectRestoreInfo2)} cannot have values. Please file an issue at NuGet/Home if you see this exception."); } if (projectRestoreInfo != null) { if (projectRestoreInfo.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } else { if (projectRestoreInfo2.TargetFrameworks == null) { throw new InvalidOperationException("TargetFrameworks cannot be null."); } } try { _logger.LogInformation( $"The nominate API is called for '{projectUniqueName}'."); var projectNames = ProjectNames.FromFullProjectPath(projectUniqueName); DependencyGraphSpec dgSpec; IReadOnlyList <IAssetsLogMessage> nominationErrors = null; try { dgSpec = ToDependencyGraphSpec(projectNames, projectRestoreInfo, projectRestoreInfo2); } catch (Exception e) { var restoreLogMessage = RestoreLogMessage.CreateError(NuGetLogCode.NU1105, string.Format(Resources.NU1105, projectNames.ShortName, e.Message)); restoreLogMessage.LibraryId = projectUniqueName; nominationErrors = new List <IAssetsLogMessage>() { AssetsLogMessage.Create(restoreLogMessage) }; var projectDirectory = Path.GetDirectoryName(projectUniqueName); string projectIntermediatePath = projectRestoreInfo == null ? projectRestoreInfo2.BaseIntermediatePath : projectRestoreInfo.BaseIntermediatePath; var dgSpecOutputPath = GetProjectOutputPath(projectDirectory, projectIntermediatePath); dgSpec = CreateMinimalDependencyGraphSpec(projectUniqueName, dgSpecOutputPath); } _projectSystemCache.AddProjectRestoreInfo(projectNames, dgSpec, nominationErrors); // returned task completes when scheduled restore operation completes. var restoreTask = _restoreWorker.ScheduleRestoreAsync( SolutionRestoreRequest.OnUpdate(), token); return(restoreTask); } catch (OperationCanceledException) { throw; } catch (Exception e) { _logger.LogError(e.ToString()); TelemetryUtility.EmitException(nameof(VsSolutionRestoreService), nameof(NominateProjectAsync), e); return(Task.FromResult(false)); } }
private async Task <bool> StartBackgroundJobRunnerAsync(CancellationToken token) { // Hops onto a background pool thread await TaskScheduler.Default; var status = false; // Check if the solution is fully loaded while (!_solutionLoadedEvent.IsSet) { // Needed when OnAfterBackgroundSolutionLoadComplete fires before // Advise has been called. if (await IsSolutionFullyLoadedAsync()) { _solutionLoadedEvent.Set(); break; } else { // Waits for 100ms to let solution fully load or canceled await _solutionLoadedEvent.WaitAsync() .WithTimeout(TimeSpan.FromMilliseconds(DelaySolutionLoadRetry)) .WithCancellation(token); } } // Loops until there are pending restore requests or it's get cancelled while (!token.IsCancellationRequested) { lock (_lockPendingRequestsObj) { // if no pending restore requests then shut down the restore job runner. if (_pendingRequests.Value.Count == 0) { break; } } // Grabs a local copy of pending restore operation using (var restoreOperation = _pendingRestore) { try { // Blocks the execution until first request is scheduled // Monitors the cancelllation token as well. var request = _pendingRequests.Value.Take(token); token.ThrowIfCancellationRequested(); // Claims the ownership over the active task // Awaits for currently running restore to complete await PromoteTaskToActiveAsync(restoreOperation, token); token.ThrowIfCancellationRequested(); DateTime lastNominationReceived = DateTime.UtcNow; // Drains the queue while (!_pendingRequests.Value.IsCompleted && !token.IsCancellationRequested) { SolutionRestoreRequest next; // check if there are pending nominations var isAllProjectsNominated = await _solutionManager.Value.IsAllProjectsNominatedAsync(); // Try to get a request without a timeout. We don't want to *block* the threadpool thread. if (!_pendingRequests.Value.TryTake(out next, millisecondsTimeout: 0, token)) { if (isAllProjectsNominated) { // if we've got all the nominations then continue with the auto restore break; } else { // Break if we've waited for more than 10s without an actual nomination. if (lastNominationReceived.AddMilliseconds(MaxIdleWaitTimeMs) < DateTime.UtcNow) { break; } await Task.Delay(IdleTimeoutMs, token); } } else { lastNominationReceived = DateTime.UtcNow; // Upgrade request if necessary if (next != null && next.RestoreSource != request.RestoreSource) { // there could be requests of two types: Auto-Restore or Explicit // Explicit is always preferred. request = new SolutionRestoreRequest( next.ForceRestore || request.ForceRestore, RestoreOperationSource.Explicit); // we don't want to delay explicit solution restore request so just break at this time. break; } } } token.ThrowIfCancellationRequested(); // Replaces pending restore operation with a new one. // Older value is ignored. var ignore = Interlocked.CompareExchange( ref _pendingRestore, new BackgroundRestoreOperation(), restoreOperation); token.ThrowIfCancellationRequested(); // Runs restore job with scheduled request params status = await ProcessRestoreRequestAsync(restoreOperation, request, token); // Repeats... } catch (OperationCanceledException) when(token.IsCancellationRequested) { // Ignores } catch (Exception e) { // Writes stack to activity log Logger.LogError(e.ToString()); // Do not die just yet } } } return(status); }
private async Task <bool> StartBackgroundJobRunnerAsync(CancellationToken token) { // Hops onto a background pool thread await TaskScheduler.Default; // Waits until the solution is fully loaded or canceled await _solutionLoadedEvent.WaitAsync().WithCancellation(token); // Loops forever until it's get cancelled while (!token.IsCancellationRequested) { // Grabs a local copy of pending restore operation using (var restoreOperation = _pendingRestore) { try { // Blocks the execution until first request is scheduled // Monitors the cancelllation token as well. var request = _pendingRequests.Value.Take(token); token.ThrowIfCancellationRequested(); // Claims the ownership over the active task // Awaits for currently running restore to complete await PromoteTaskToActiveAsync(restoreOperation, token); token.ThrowIfCancellationRequested(); var retries = 0; // Drains the queue while (!_pendingRequests.Value.IsCompleted && !token.IsCancellationRequested) { SolutionRestoreRequest next; // check if there are pending nominations var isAllProjectsNominated = _solutionManager.Value.IsAllProjectsNominated(); if (!_pendingRequests.Value.TryTake(out next, IdleTimeoutMs, token)) { if (isAllProjectsNominated) { // if we've got all the nominations then continue with the auto restore break; } } // Upgrade request if necessary if (next.RestoreSource != request.RestoreSource) { // there could be requests of two types: Auto-Restore or Explicit // Explicit is always preferred. request = new SolutionRestoreRequest( next.ForceRestore || request.ForceRestore, RestoreOperationSource.Explicit); // we don't want to delay explicit solution restore request so just break at this time. break; } if (!isAllProjectsNominated) { if (retries >= DelayAutoRestoreRetries) { // we're still missing some nominations but don't delay it indefinitely and let auto restore fail. // we wait until 20 secs for all the projects to be nominated at solution load. break; } // if we're still expecting some nominations and also haven't reached our max timeout // then increase the retries count. retries++; } } token.ThrowIfCancellationRequested(); // Replaces pending restore operation with a new one. // Older value is ignored. var ignore = Interlocked.CompareExchange( ref _pendingRestore, new BackgroundRestoreOperation(), restoreOperation); token.ThrowIfCancellationRequested(); // Runs restore job with scheduled request params await ProcessRestoreRequestAsync(restoreOperation, request, token); // Repeats... } catch (OperationCanceledException) when(token.IsCancellationRequested) { // Ignores } catch (Exception e) { // Writes stack to activity log Logger.LogError(e.ToString()); // Do not die just yet } } } // returns false on preliminary exit (cancellation) return(false); }
private async Task <bool> StartBackgroundJobRunnerAsync(CancellationToken token) { // Hops onto a background pool thread await TaskScheduler.Default; // Waits until the solution is fully loaded or canceled await _solutionLoadedEvent.WaitAsync().WithCancellation(token); // Loops forever until it's get cancelled while (!token.IsCancellationRequested) { // Grabs a local copy of pending restore operation using (var restoreOperation = _pendingRestore) { try { // Blocks the execution until first request is scheduled // Monitors the cancelllation token as well. var request = _pendingRequests.Value.Take(token); token.ThrowIfCancellationRequested(); // Claims the ownership over the active task // Awaits for currently running restore to complete await PromoteTaskToActiveAsync(restoreOperation, token); token.ThrowIfCancellationRequested(); // Drains the queue while (!_pendingRequests.Value.IsCompleted && !token.IsCancellationRequested) { SolutionRestoreRequest next; if (!_pendingRequests.Value.TryTake(out next, IdleTimeoutMs, token)) { break; } // Upgrade request if necessary if (next.RestoreSource != request.RestoreSource) { // there could be requests of two types: Auto-Restore or Explicit // Explicit is always preferred. request = new SolutionRestoreRequest( next.ForceRestore || request.ForceRestore, RestoreOperationSource.Explicit); } } token.ThrowIfCancellationRequested(); // Replaces pending restore operation with a new one. // Older value is ignored. var ignore = Interlocked.CompareExchange( ref _pendingRestore, new BackgroundRestoreOperation(), restoreOperation); token.ThrowIfCancellationRequested(); // Runs restore job with scheduled request params await ProcessRestoreRequestAsync(restoreOperation, request, token); // Repeats... } catch (OperationCanceledException) when(token.IsCancellationRequested) { // Ignores } catch (Exception e) { // Writes stack to activity log Logger.LogError(e.ToString()); // Do not die just yet } } } // returns false on preliminary exit (cancellation) return(false); }
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 IsBusy to true _isCompleteEvent.Reset(); try { // Initialize if not already done. await InitializeAsync(); var pendingRestore = _pendingRestore; var shouldStartNewBGJobRunner = true; // 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) { // 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); } // on-board request onto pending restore operation _pendingRequests.Value.TryAdd(request); } try { using (_joinableCollection.Join()) { // when there is no current background restore job running, then it will start a new one. // else, current requrest will also await the existing job to be completed. if (shouldStartNewBGJobRunner) { _backgroundJobRunner = new AsyncLazy <bool>( () => StartBackgroundJobRunnerAsync(_workerCts.Token), _joinableFactory); } // 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(); } }
int IVsTrackBatchRetargetingEvents.OnBatchRetargetingEnd() { NuGetProject nuGetProject = null; NuGetUIThreadHelper.JoinableTaskFactory.Run(async delegate { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); _errorListProvider.Tasks.Clear(); if (_platformRetargetingProject != null) { try { var project = _dte.Solution.Item(_platformRetargetingProject); if (project != null) { nuGetProject = EnvDTEProjectUtility.GetNuGetProject(project, _solutionManager); if (ProjectRetargetingUtility.IsProjectRetargetable(nuGetProject)) { var frameworkName = EnvDTEProjectUtility.GetTargetFrameworkString(project); if (NETCore451.Equals(frameworkName, StringComparison.OrdinalIgnoreCase) || Windows81.Equals(frameworkName, StringComparison.OrdinalIgnoreCase)) { IList <PackageIdentity> packagesToBeReinstalled = await ProjectRetargetingUtility.GetPackagesToBeReinstalled(nuGetProject); if (packagesToBeReinstalled.Count > 0) { // By asserting that NuGet is in use, we are also asserting that NuGet.VisualStudio.dll is already loaded // Hence, it is okay to call project.ToVsHierarchy() Debug.Assert(ProjectRetargetingUtility.IsNuGetInUse(project)); IVsHierarchy projectHierarchy = VsHierarchyUtility.ToVsHierarchy(project); ShowRetargetingErrorTask(packagesToBeReinstalled.Select(p => p.Id), projectHierarchy, TaskErrorCategory.Error, TaskPriority.High); } ProjectRetargetingUtility.MarkPackagesForReinstallation(nuGetProject, packagesToBeReinstalled); } } } } catch (ArgumentException) { // If the solution does not contain a project named '_platformRetargetingProject', it will throw ArgumentException } _platformRetargetingProject = null; } }); #if !VS14 if (nuGetProject != null && nuGetProject is LegacyCSProjPackageReferenceProject) { // trigger solution restore and don't wait for it to be complete and hold the UI thread System.Threading.Tasks.Task.Run(() => _solutionRestoreWorker.Value.ScheduleRestoreAsync(SolutionRestoreRequest.ByMenu(), CancellationToken.None)); } #endif return(VSConstants.S_OK); }
int IVsTrackProjectRetargetingEvents.OnRetargetingAfterChange(string projRef, IVsHierarchy pAfterChangeHier, string fromTargetFramework, string toTargetFramework) { NuGetProject retargetedProject = null; NuGetUIThreadHelper.JoinableTaskFactory.Run(async delegate { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); _errorListProvider.Tasks.Clear(); var project = VsHierarchyUtility.GetProjectFromHierarchy(pAfterChangeHier); retargetedProject = EnvDTEProjectUtility.GetNuGetProject(project, _solutionManager); if (ProjectRetargetingUtility.IsProjectRetargetable(retargetedProject)) { var packagesToBeReinstalled = await ProjectRetargetingUtility.GetPackagesToBeReinstalled(retargetedProject); if (packagesToBeReinstalled.Any()) { ShowRetargetingErrorTask(packagesToBeReinstalled.Select(p => p.Id), pAfterChangeHier, TaskErrorCategory.Error, TaskPriority.High); } ProjectRetargetingUtility.MarkPackagesForReinstallation(retargetedProject, packagesToBeReinstalled); } }); #if !VS14 if (retargetedProject != null && retargetedProject is LegacyCSProjPackageReferenceProject) { // trigger solution restore and don't wait for it to be complete and hold the UI thread System.Threading.Tasks.Task.Run(() => _solutionRestoreWorker.Value.ScheduleRestoreAsync(SolutionRestoreRequest.ByMenu(), CancellationToken.None)); } #endif return(VSConstants.S_OK); }