public Task ReleaseSubscriptionsAsync() { _currentProjectContext = null; foreach (var link in _evaluationSubscriptionLinks.Concat(_designTimeBuildSubscriptionLinks)) { link.Dispose(); } _evaluationSubscriptionLinks.Clear(); _designTimeBuildSubscriptionLinks.Clear(); return(Task.CompletedTask); }
private async Task UpdateProjectContextAndSubscriptionsAsync() { AggregateCrossTargetProjectContext previousProjectContext = await ExecuteWithinLockAsync(() => _currentAggregateProjectContext); AggregateCrossTargetProjectContext newProjectContext = await UpdateProjectContextAsync(); if (previousProjectContext != newProjectContext) { // Dispose existing subscriptions. DisposeAndClearSubscriptions(); // Add subscriptions for the configured projects in the new project context. await AddSubscriptionsAsync(newProjectContext); } }
public void ReleaseProjectContext(AggregateCrossTargetProjectContext context) { Requires.NotNull(context, nameof(context)); lock (_gate) { if (!_contexts.Remove(context)) { throw new ArgumentException("Specified context was not created by this instance, or has already been unregistered."); } // Update the maps storing configured project host objects and project contexts which are shared across created contexts. // We can remove the ones which are only used by the current context being released. RemoveUnusedConfiguredProjectsState_NoLock(); } }
public Task AddSubscriptionsAsync(AggregateCrossTargetProjectContext newProjectContext) { Requires.NotNull(newProjectContext, nameof(newProjectContext)); _currentProjectContext = newProjectContext; var watchedEvaluationRules = GetWatchedRules(RuleHandlerType.Evaluation); var watchedDesignTimeBuildRules = GetWatchedRules(RuleHandlerType.DesignTimeBuild); foreach (var configuredProject in newProjectContext.InnerConfiguredProjects) { SubscribeToConfiguredProject( configuredProject.Services.ProjectSubscription, watchedEvaluationRules, watchedDesignTimeBuildRules); } return(Task.CompletedTask); }
protected override void Handle( AggregateCrossTargetProjectContext currentAggregateContext, ITargetFramework targetFrameworkToUpdate, EventData e) { IProjectSharedFoldersSnapshot sharedProjectsUpdate = e.Item2; IProjectCatalogSnapshot catalogs = e.Item3; var changesBuilder = new DependenciesChangesBuilder(); ProcessSharedProjectsUpdates(sharedProjectsUpdate, targetFrameworkToUpdate, changesBuilder); IDependenciesChanges?changes = changesBuilder.TryBuildChanges(); if (changes != null) { RaiseDependenciesChanged(targetFrameworkToUpdate, changes, currentAggregateContext, catalogs); } }
private async Task HandleAsync(Tuple <IProjectSubscriptionUpdate, IProjectSharedFoldersSnapshot, IProjectCatalogSnapshot> e) { AggregateCrossTargetProjectContext currentAggregateContext = await _host.GetCurrentAggregateProjectContext(); if (currentAggregateContext == null) { return; } IProjectSubscriptionUpdate projectUpdate = e.Item1; IProjectSharedFoldersSnapshot sharedProjectsUpdate = e.Item2; IProjectCatalogSnapshot catalogs = e.Item3; // We need to process the update within a lock to ensure that we do not release this context during processing. // TODO: Enable concurrent execution of updates themselves, i.e. two separate invocations of HandleAsync // should be able to run concurrently. using (await _gate.DisposableWaitAsync()) { // Get the inner workspace project context to update for this change. ITargetedProjectContext projectContextToUpdate = currentAggregateContext .GetInnerProjectContext(projectUpdate.ProjectConfiguration, out bool isActiveContext); if (projectContextToUpdate == null) { return; } var dependencyChangeContext = new DependenciesRuleChangeContext( currentAggregateContext.ActiveProjectContext.TargetFramework, catalogs); ProcessSharedProjectsUpdates(sharedProjectsUpdate, projectContextToUpdate, dependencyChangeContext); if (dependencyChangeContext.AnyChanges) { DependenciesChanged?.Invoke(this, new DependencySubscriptionChangedEventArgs(dependencyChangeContext)); } } }
public void AddSubscriptions(AggregateCrossTargetProjectContext newProjectContext) { Requires.NotNull(newProjectContext, nameof(newProjectContext)); _currentProjectContext = newProjectContext; IEnumerable <string> watchedEvaluationRules = GetWatchedRules(RuleHandlerType.Evaluation); IEnumerable <string> watchedDesignTimeBuildRules = GetWatchedRules(RuleHandlerType.DesignTimeBuild); // initialize telemetry with all rules for each target framework foreach (ITargetedProjectContext projectContext in newProjectContext.InnerProjectContexts) { _treeTelemetryService.InitializeTargetFrameworkRules(projectContext.TargetFramework, watchedEvaluationRules); _treeTelemetryService.InitializeTargetFrameworkRules(projectContext.TargetFramework, watchedDesignTimeBuildRules); } foreach (ConfiguredProject configuredProject in newProjectContext.InnerConfiguredProjects) { SubscribeToConfiguredProject( configuredProject, configuredProject.Services.ProjectSubscription, watchedEvaluationRules, watchedDesignTimeBuildRules); } }
public async Task <AggregateCrossTargetProjectContext> CreateProjectContextAsync() { EnsureInitialized(); AggregateCrossTargetProjectContext context = await CreateProjectContextAsyncCore().ConfigureAwait(false); if (context == null) { return(null); } lock (_gate) { // There's a race here, by the time we've created the project context, // the project could have been renamed, handle this. ProjectData projectData = GetProjectData(); context.SetProjectFilePathAndDisplayName(projectData.FullPath, projectData.DisplayName); _contexts.Add(context); } return(context); }
private async Task HandleAsync(Tuple <IProjectSubscriptionUpdate, IProjectSharedFoldersSnapshot, IProjectCatalogSnapshot> e) { AggregateCrossTargetProjectContext currentAggregateContext = await _host.GetCurrentAggregateProjectContextAsync(); if (currentAggregateContext == null) { return; } IProjectSubscriptionUpdate projectUpdate = e.Item1; IProjectSharedFoldersSnapshot sharedProjectsUpdate = e.Item2; IProjectCatalogSnapshot catalogs = e.Item3; // Get the target framework to update for this change. ITargetFramework targetFrameworkToUpdate = currentAggregateContext.GetProjectFramework(projectUpdate.ProjectConfiguration); if (targetFrameworkToUpdate == null) { return; } var changesBuilder = new CrossTargetDependenciesChangesBuilder(); ProcessSharedProjectsUpdates(sharedProjectsUpdate, targetFrameworkToUpdate, changesBuilder); ImmutableDictionary <ITargetFramework, IDependenciesChanges> changes = changesBuilder.TryBuildChanges(); if (changes != null) { DependenciesChanged?.Invoke( this, new DependencySubscriptionChangedEventArgs( currentAggregateContext.ActiveTargetFramework, catalogs, changes)); } }
public Task AddSubscriptionsAsync(AggregateCrossTargetProjectContext newProjectContext) { Requires.NotNull(newProjectContext, nameof(newProjectContext)); _currentProjectContext = newProjectContext; var watchedEvaluationRules = GetWatchedRules(RuleHandlerType.Evaluation); var watchedDesignTimeBuildRules = GetWatchedRules(RuleHandlerType.DesignTimeBuild); // initialize telemetry with all rules for each target framework foreach (var projectContext in newProjectContext.InnerProjectContexts) { _treeTelemetryService.InitializeTargetFrameworkRules(projectContext.TargetFramework, watchedEvaluationRules); _treeTelemetryService.InitializeTargetFrameworkRules(projectContext.TargetFramework, watchedDesignTimeBuildRules); } foreach (var configuredProject in newProjectContext.InnerConfiguredProjects) { SubscribeToConfiguredProject( configuredProject, configuredProject.Services.ProjectSubscription, watchedEvaluationRules, watchedDesignTimeBuildRules); } return(Task.CompletedTask); }
private async Task AddSubscriptionsAsync(AggregateCrossTargetProjectContext newProjectContext) { Requires.NotNull(newProjectContext, nameof(newProjectContext)); await _commonServices.ThreadingService.SwitchToUIThread(); using (_tasksService.LoadedProject()) { lock (_linksLock) { foreach (var configuredProject in newProjectContext.InnerConfiguredProjects) { SubscribeToConfiguredProject(configuredProject.Services.ProjectSubscription, new ActionBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >( e => OnProjectChangedCoreAsync(e, RuleHandlerType.Evaluation))); } foreach (var subscriber in Subscribers) { subscriber.Value.AddSubscriptionsAsync(newProjectContext); } } } }
protected virtual void OnAggregateContextChanged(AggregateCrossTargetProjectContext oldContext, AggregateCrossTargetProjectContext newContext) { // by default do nothing }
private async Task HandleAsync( IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > e, RuleHandlerType handlerType) { AggregateCrossTargetProjectContext currentAggregateContext = await _host.GetCurrentAggregateProjectContext().ConfigureAwait(false); if (currentAggregateContext == null || _currentProjectContext != currentAggregateContext) { return; } IProjectSubscriptionUpdate update = e.Value.Item1; IProjectCatalogSnapshot catalogs = e.Value.Item2; IEnumerable <ICrossTargetRuleHandler <T> > handlers = Handlers.Select(h => h.Value) .Where(h => h.SupportsHandlerType(handlerType)); // We need to process the update within a lock to ensure that we do not release this context during processing. // TODO: Enable concurrent execution of updates themeselves, i.e. two separate invocations of HandleAsync // should be able to run concurrently. using (await _gate.DisposableWaitAsync().ConfigureAwait(true)) { // Get the inner workspace project context to update for this change. ITargetedProjectContext projectContextToUpdate = currentAggregateContext .GetInnerProjectContext(update.ProjectConfiguration, out bool isActiveContext); if (projectContextToUpdate == null) { return; } // Broken design time builds sometimes cause updates with no project changes and sometimes // cause updates with a project change that has no difference. // We handle the former case here, and the latter case is handled in the CommandLineItemHandler. if (update.ProjectChanges.Count == 0) { if (handlerType == RuleHandlerType.DesignTimeBuild) { projectContextToUpdate.LastDesignTimeBuildSucceeded = false; } return; } T ruleChangeContext = CreateRuleChangeContext( currentAggregateContext.ActiveProjectContext.TargetFramework, catalogs); foreach (ICrossTargetRuleHandler <T> handler in handlers) { ImmutableDictionary <string, IProjectChangeDescription> .Builder builder = ImmutableDictionary.CreateBuilder <string, IProjectChangeDescription>(StringComparers.RuleNames); ImmutableHashSet <string> handlerRules = handler.GetRuleNames(handlerType); builder.AddRange(update.ProjectChanges.Where( x => handlerRules.Contains(x.Key))); ImmutableDictionary <string, IProjectChangeDescription> projectChanges = builder.ToImmutable(); if (handler.ReceiveUpdatesWithEmptyProjectChange || projectChanges.Any(x => x.Value.Difference.AnyChanges)) { await handler.HandleAsync(e, projectChanges, projectContextToUpdate, isActiveContext, ruleChangeContext) .ConfigureAwait(true); } } await CompleteHandleAsync(ruleChangeContext).ConfigureAwait(false); // record all the rules that have occurred _treeTelemetryService.ObserveTargetFrameworkRules(projectContextToUpdate.TargetFramework, update.ProjectChanges.Keys); } }
private async Task HandleAsync( IProjectSubscriptionUpdate projectUpdate, IProjectCatalogSnapshot catalogSnapshot, RuleHandlerType handlerType) { AggregateCrossTargetProjectContext currentAggregateContext = await _host.GetCurrentAggregateProjectContext(); if (currentAggregateContext == null || _currentProjectContext != currentAggregateContext) { return; } IEnumerable <ICrossTargetRuleHandler <T> > handlers = Handlers .Select(h => h.Value) .Where(h => h.SupportsHandlerType(handlerType)); ITargetedProjectContext projectContextToUpdate; // We need to process the update within a lock to ensure that we do not release this context during processing. // TODO: Enable concurrent execution of updates themselves, i.e. two separate invocations of HandleAsync // should be able to run concurrently. using (await _gate.DisposableWaitAsync()) { // Get the inner workspace project context to update for this change. projectContextToUpdate = currentAggregateContext .GetInnerProjectContext(projectUpdate.ProjectConfiguration, out bool isActiveContext); if (projectContextToUpdate == null) { return; } // Broken design time builds sometimes cause updates with no project changes and sometimes // cause updates with a project change that has no difference. // We handle the former case here, and the latter case is handled in the CommandLineItemHandler. if (projectUpdate.ProjectChanges.Count == 0) { if (handlerType == RuleHandlerType.DesignTimeBuild) { projectContextToUpdate.LastDesignTimeBuildSucceeded = false; } return; } // Get the subclass-specific context that will aggregate data during the processing of rule data by handlers. T ruleChangeContext = CreateRuleChangeContext( currentAggregateContext.ActiveProjectContext.TargetFramework, catalogSnapshot); // Give each handler a chance to modify the rule change context. foreach (ICrossTargetRuleHandler <T> handler in handlers) { ImmutableHashSet <string> handlerRules = handler.GetRuleNames(handlerType); // Slice project changes to include only rules the handler claims an interest in. var projectChanges = projectUpdate.ProjectChanges .Where(x => handlerRules.Contains(x.Key)) .ToImmutableDictionary(); if (handler.ReceiveUpdatesWithEmptyProjectChange || projectChanges.Any(x => x.Value.Difference.AnyChanges)) { // Handlers respond to rule changes in a way that's specific to the rule change context // type (T). For example, DependencyRulesSubscriber uses DependenciesRuleChangeContext // which holds IDependencyModel, so its ICrossTargetRuleHandler<DependenciesRuleChangeContext> // implementations will produce IDependencyModel objects in response to rule changes. handler.Handle(projectChanges, projectContextToUpdate.TargetFramework, ruleChangeContext); } } // Notify the subclass that their rule change context object is ready for finalization. CompleteHandle(ruleChangeContext); } // record all the rules that have occurred _treeTelemetryService.ObserveTargetFrameworkRules(projectContextToUpdate.TargetFramework, projectUpdate.ProjectChanges.Keys); }
private async Task DisposeAggregateProjectContextAsync(AggregateCrossTargetProjectContext projectContext) { await _contextProvider.Value.ReleaseProjectContextAsync(projectContext); }
private void DisposeAggregateProjectContext(AggregateCrossTargetProjectContext projectContext) { _contextProvider.Value.ReleaseProjectContext(projectContext); }