提交 67737aeb 编写于 作者: M Matt Warren

Push task queue to CPSProject

上级 003c3201
...@@ -557,6 +557,8 @@ protected bool CanConvertToProjectReferences ...@@ -557,6 +557,8 @@ protected bool CanConvertToProjectReferences
protected int AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(string filePath, MetadataReferenceProperties properties) protected int AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(string filePath, MetadataReferenceProperties properties)
{ {
AssertIsForeground();
// If this file is coming from a project, then we should convert it to a project reference instead // If this file is coming from a project, then we should convert it to a project reference instead
if (this.CanConvertToProjectReferences && ProjectTracker.TryGetProjectByBinPath(filePath, out var project)) if (this.CanConvertToProjectReferences && ProjectTracker.TryGetProjectByBinPath(filePath, out var project))
{ {
...@@ -618,6 +620,8 @@ protected int AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(s ...@@ -618,6 +620,8 @@ protected int AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(s
protected void RemoveMetadataReference(string filePath) protected void RemoveMetadataReference(string filePath)
{ {
AssertIsForeground();
// Is this a reference we converted to a project reference? // Is this a reference we converted to a project reference?
if (TryGetMetadataFileNameToConvertedProjectReference(filePath, out var projectReference)) if (TryGetMetadataFileNameToConvertedProjectReference(filePath, out var projectReference))
{ {
...@@ -723,6 +727,8 @@ private void OnImportChangedAfterDelay(Task previous, object state) ...@@ -723,6 +727,8 @@ private void OnImportChangedAfterDelay(Task previous, object state)
private void OnAnalyzerChanged(object sender, EventArgs e) private void OnAnalyzerChanged(object sender, EventArgs e)
{ {
AssertIsForeground();
// Postpone handler's actions to prevent deadlock. This AnalyzeChanged event can // Postpone handler's actions to prevent deadlock. This AnalyzeChanged event can
// be invoked while the FileChangeService lock is held, and VisualStudioAnalyzer's // be invoked while the FileChangeService lock is held, and VisualStudioAnalyzer's
// efforts to listen to file changes can lead to a deadlock situation. // efforts to listen to file changes can lead to a deadlock situation.
...@@ -740,6 +746,8 @@ private void OnAnalyzerChanged(object sender, EventArgs e) ...@@ -740,6 +746,8 @@ private void OnAnalyzerChanged(object sender, EventArgs e)
// Internal for unit testing // Internal for unit testing
internal void AddProjectReference(ProjectReference projectReference) internal void AddProjectReference(ProjectReference projectReference)
{ {
AssertIsForeground();
// dev11 is sometimes calling us multiple times for the same data // dev11 is sometimes calling us multiple times for the same data
if (!CanAddProjectReference(projectReference)) if (!CanAddProjectReference(projectReference))
{ {
...@@ -824,6 +832,8 @@ private bool TransitivelyReferencesWorker(ProjectId projectId, HashSet<ProjectId ...@@ -824,6 +832,8 @@ private bool TransitivelyReferencesWorker(ProjectId projectId, HashSet<ProjectId
protected void RemoveProjectReference(ProjectReference projectReference) protected void RemoveProjectReference(ProjectReference projectReference)
{ {
AssertIsForeground();
lock (_gate) lock (_gate)
{ {
Contract.ThrowIfFalse(_projectReferences.Remove(projectReference)); Contract.ThrowIfFalse(_projectReferences.Remove(projectReference));
...@@ -840,6 +850,8 @@ private static void OnDocumentOpened(object sender, bool isCurrentContext) ...@@ -840,6 +850,8 @@ private static void OnDocumentOpened(object sender, bool isCurrentContext)
IVisualStudioHostDocument document = (IVisualStudioHostDocument)sender; IVisualStudioHostDocument document = (IVisualStudioHostDocument)sender;
AbstractProject project = (AbstractProject)document.Project; AbstractProject project = (AbstractProject)document.Project;
project.AssertIsForeground();
if (project._pushingChangesToWorkspaceHosts) if (project._pushingChangesToWorkspaceHosts)
{ {
project.ProjectTracker.NotifyWorkspaceHosts(host => host.OnDocumentOpened(document.Id, document.GetOpenTextBuffer(), isCurrentContext)); project.ProjectTracker.NotifyWorkspaceHosts(host => host.OnDocumentOpened(document.Id, document.GetOpenTextBuffer(), isCurrentContext));
...@@ -856,6 +868,8 @@ private static void OnDocumentClosing(object sender, bool updateActiveContext) ...@@ -856,6 +868,8 @@ private static void OnDocumentClosing(object sender, bool updateActiveContext)
AbstractProject project = (AbstractProject)document.Project; AbstractProject project = (AbstractProject)document.Project;
var projectTracker = project.ProjectTracker; var projectTracker = project.ProjectTracker;
project.AssertIsForeground();
if (project._pushingChangesToWorkspaceHosts) if (project._pushingChangesToWorkspaceHosts)
{ {
projectTracker.NotifyWorkspaceHosts(host => host.OnDocumentClosed(document.Id, document.GetOpenTextBuffer(), document.Loader, updateActiveContext)); projectTracker.NotifyWorkspaceHosts(host => host.OnDocumentClosed(document.Id, document.GetOpenTextBuffer(), document.Loader, updateActiveContext));
...@@ -867,6 +881,8 @@ private static void OnDocumentUpdatedOnDisk(object sender, EventArgs e) ...@@ -867,6 +881,8 @@ private static void OnDocumentUpdatedOnDisk(object sender, EventArgs e)
IVisualStudioHostDocument document = (IVisualStudioHostDocument)sender; IVisualStudioHostDocument document = (IVisualStudioHostDocument)sender;
AbstractProject project = (AbstractProject)document.Project; AbstractProject project = (AbstractProject)document.Project;
project.AssertIsForeground();
if (project._pushingChangesToWorkspaceHosts) if (project._pushingChangesToWorkspaceHosts)
{ {
project.ProjectTracker.NotifyWorkspaceHosts(host => host.OnDocumentTextUpdatedOnDisk(document.Id)); project.ProjectTracker.NotifyWorkspaceHosts(host => host.OnDocumentTextUpdatedOnDisk(document.Id));
...@@ -878,6 +894,8 @@ private static void OnAdditionalDocumentOpened(object sender, bool isCurrentCont ...@@ -878,6 +894,8 @@ private static void OnAdditionalDocumentOpened(object sender, bool isCurrentCont
IVisualStudioHostDocument document = (IVisualStudioHostDocument)sender; IVisualStudioHostDocument document = (IVisualStudioHostDocument)sender;
AbstractProject project = (AbstractProject)document.Project; AbstractProject project = (AbstractProject)document.Project;
project.AssertIsForeground();
if (project._pushingChangesToWorkspaceHosts) if (project._pushingChangesToWorkspaceHosts)
{ {
project.ProjectTracker.NotifyWorkspaceHosts(host => host.OnAdditionalDocumentOpened(document.Id, document.GetOpenTextBuffer(), isCurrentContext)); project.ProjectTracker.NotifyWorkspaceHosts(host => host.OnAdditionalDocumentOpened(document.Id, document.GetOpenTextBuffer(), isCurrentContext));
...@@ -894,6 +912,8 @@ private static void OnAdditionalDocumentClosing(object sender, bool notUsed) ...@@ -894,6 +912,8 @@ private static void OnAdditionalDocumentClosing(object sender, bool notUsed)
AbstractProject project = (AbstractProject)document.Project; AbstractProject project = (AbstractProject)document.Project;
var projectTracker = project.ProjectTracker; var projectTracker = project.ProjectTracker;
project.AssertIsForeground();
if (project._pushingChangesToWorkspaceHosts) if (project._pushingChangesToWorkspaceHosts)
{ {
projectTracker.NotifyWorkspaceHosts(host => host.OnAdditionalDocumentClosed(document.Id, document.GetOpenTextBuffer(), document.Loader)); projectTracker.NotifyWorkspaceHosts(host => host.OnAdditionalDocumentClosed(document.Id, document.GetOpenTextBuffer(), document.Loader));
...@@ -905,6 +925,8 @@ private static void OnAdditionalDocumentUpdatedOnDisk(object sender, EventArgs e ...@@ -905,6 +925,8 @@ private static void OnAdditionalDocumentUpdatedOnDisk(object sender, EventArgs e
IVisualStudioHostDocument document = (IVisualStudioHostDocument)sender; IVisualStudioHostDocument document = (IVisualStudioHostDocument)sender;
AbstractProject project = (AbstractProject)document.Project; AbstractProject project = (AbstractProject)document.Project;
project.AssertIsForeground();
if (project._pushingChangesToWorkspaceHosts) if (project._pushingChangesToWorkspaceHosts)
{ {
project.ProjectTracker.NotifyWorkspaceHosts(host => host.OnAdditionalDocumentTextUpdatedOnDisk(document.Id)); project.ProjectTracker.NotifyWorkspaceHosts(host => host.OnAdditionalDocumentTextUpdatedOnDisk(document.Id));
...@@ -917,6 +939,8 @@ private static void OnAdditionalDocumentUpdatedOnDisk(object sender, EventArgs e ...@@ -917,6 +939,8 @@ private static void OnAdditionalDocumentUpdatedOnDisk(object sender, EventArgs e
Func<IVisualStudioHostDocument, bool> getIsCurrentContext, Func<IVisualStudioHostDocument, bool> getIsCurrentContext,
Func<uint, IReadOnlyList<string>> getFolderNames) Func<uint, IReadOnlyList<string>> getFolderNames)
{ {
AssertIsForeground();
// We can currently be on a background thread. // We can currently be on a background thread.
// So, hookup the handlers when creating the standard text document, as we might receive these handler notifications on the UI thread. // So, hookup the handlers when creating the standard text document, as we might receive these handler notifications on the UI thread.
var document = this.DocumentProvider.TryGetDocumentForFile( var document = this.DocumentProvider.TryGetDocumentForFile(
...@@ -957,6 +981,8 @@ protected void AddUntrackedFile(string filename) ...@@ -957,6 +981,8 @@ protected void AddUntrackedFile(string filename)
protected void RemoveFile(string filename) protected void RemoveFile(string filename)
{ {
AssertIsForeground();
lock (_gate) lock (_gate)
{ {
// Remove this as an untracked file, if it is // Remove this as an untracked file, if it is
...@@ -977,6 +1003,8 @@ protected void RemoveFile(string filename) ...@@ -977,6 +1003,8 @@ protected void RemoveFile(string filename)
internal void AddDocument(IVisualStudioHostDocument document, bool isCurrentContext, bool hookupHandlers) internal void AddDocument(IVisualStudioHostDocument document, bool isCurrentContext, bool hookupHandlers)
{ {
AssertIsForeground();
// We do not want to allow message pumping/reentrancy when processing project system changes. // We do not want to allow message pumping/reentrancy when processing project system changes.
using (Dispatcher.CurrentDispatcher.DisableProcessing()) using (Dispatcher.CurrentDispatcher.DisableProcessing())
{ {
...@@ -1014,6 +1042,8 @@ internal void AddDocument(IVisualStudioHostDocument document, bool isCurrentCont ...@@ -1014,6 +1042,8 @@ internal void AddDocument(IVisualStudioHostDocument document, bool isCurrentCont
internal void RemoveDocument(IVisualStudioHostDocument document) internal void RemoveDocument(IVisualStudioHostDocument document)
{ {
AssertIsForeground();
// We do not want to allow message pumping/reentrancy when processing project system changes. // We do not want to allow message pumping/reentrancy when processing project system changes.
using (Dispatcher.CurrentDispatcher.DisableProcessing()) using (Dispatcher.CurrentDispatcher.DisableProcessing())
{ {
...@@ -1030,6 +1060,8 @@ internal void RemoveDocument(IVisualStudioHostDocument document) ...@@ -1030,6 +1060,8 @@ internal void RemoveDocument(IVisualStudioHostDocument document)
internal void AddAdditionalDocument(IVisualStudioHostDocument document, bool isCurrentContext) internal void AddAdditionalDocument(IVisualStudioHostDocument document, bool isCurrentContext)
{ {
AssertIsForeground();
lock (_gate) lock (_gate)
{ {
_additionalDocuments.Add(document.Id, document); _additionalDocuments.Add(document.Id, document);
...@@ -1056,6 +1088,8 @@ internal void AddAdditionalDocument(IVisualStudioHostDocument document, bool isC ...@@ -1056,6 +1088,8 @@ internal void AddAdditionalDocument(IVisualStudioHostDocument document, bool isC
internal void RemoveAdditionalDocument(IVisualStudioHostDocument document) internal void RemoveAdditionalDocument(IVisualStudioHostDocument document)
{ {
AssertIsForeground();
lock (_gate) lock (_gate)
{ {
_additionalDocuments.Remove(document.Id); _additionalDocuments.Remove(document.Id);
...@@ -1131,6 +1165,8 @@ public virtual void Disconnect() ...@@ -1131,6 +1165,8 @@ public virtual void Disconnect()
internal void TryProjectConversionForIntroducedOutputPath(string binPath, AbstractProject projectToReference) internal void TryProjectConversionForIntroducedOutputPath(string binPath, AbstractProject projectToReference)
{ {
AssertIsForeground();
if (this.CanConvertToProjectReferences) if (this.CanConvertToProjectReferences)
{ {
// We should not already have references for this, since we're only introducing the path for the first time // We should not already have references for this, since we're only introducing the path for the first time
...@@ -1157,6 +1193,8 @@ internal void TryProjectConversionForIntroducedOutputPath(string binPath, Abstra ...@@ -1157,6 +1193,8 @@ internal void TryProjectConversionForIntroducedOutputPath(string binPath, Abstra
internal void UndoProjectReferenceConversionForDisappearingOutputPath(string binPath) internal void UndoProjectReferenceConversionForDisappearingOutputPath(string binPath)
{ {
AssertIsForeground();
if (TryGetMetadataFileNameToConvertedProjectReference(binPath, out var projectReference)) if (TryGetMetadataFileNameToConvertedProjectReference(binPath, out var projectReference))
{ {
// We converted this, so convert it back to a metadata reference // We converted this, so convert it back to a metadata reference
...@@ -1175,6 +1213,8 @@ internal void UndoProjectReferenceConversionForDisappearingOutputPath(string bin ...@@ -1175,6 +1213,8 @@ internal void UndoProjectReferenceConversionForDisappearingOutputPath(string bin
protected void UpdateMetadataReferenceAliases(string file, ImmutableArray<string> aliases) protected void UpdateMetadataReferenceAliases(string file, ImmutableArray<string> aliases)
{ {
AssertIsForeground();
file = FileUtilities.NormalizeAbsolutePath(file); file = FileUtilities.NormalizeAbsolutePath(file);
// Have we converted these to project references? // Have we converted these to project references?
...@@ -1198,6 +1238,8 @@ protected void UpdateMetadataReferenceAliases(string file, ImmutableArray<string ...@@ -1198,6 +1238,8 @@ protected void UpdateMetadataReferenceAliases(string file, ImmutableArray<string
protected void UpdateProjectReferenceAliases(AbstractProject referencedProject, ImmutableArray<string> aliases) protected void UpdateProjectReferenceAliases(AbstractProject referencedProject, ImmutableArray<string> aliases)
{ {
AssertIsForeground();
var projectReference = GetCurrentProjectReferences().Single(r => r.ProjectId == referencedProject.Id); var projectReference = GetCurrentProjectReferences().Single(r => r.ProjectId == referencedProject.Id);
var newProjectReference = new ProjectReference(referencedProject.Id, aliases, projectReference.EmbedInteropTypes); var newProjectReference = new ProjectReference(referencedProject.Id, aliases, projectReference.EmbedInteropTypes);
...@@ -1217,6 +1259,8 @@ protected void UpdateProjectReferenceAliases(AbstractProject referencedProject, ...@@ -1217,6 +1259,8 @@ protected void UpdateProjectReferenceAliases(AbstractProject referencedProject,
private void UninitializeDocument(IVisualStudioHostDocument document) private void UninitializeDocument(IVisualStudioHostDocument document)
{ {
AssertIsForeground();
if (_pushingChangesToWorkspaceHosts) if (_pushingChangesToWorkspaceHosts)
{ {
if (document.IsOpen) if (document.IsOpen)
...@@ -1236,6 +1280,8 @@ private void UninitializeDocument(IVisualStudioHostDocument document) ...@@ -1236,6 +1280,8 @@ private void UninitializeDocument(IVisualStudioHostDocument document)
private void UninitializeAdditionalDocument(IVisualStudioHostDocument document) private void UninitializeAdditionalDocument(IVisualStudioHostDocument document)
{ {
AssertIsForeground();
if (_pushingChangesToWorkspaceHosts) if (_pushingChangesToWorkspaceHosts)
{ {
if (document.IsOpen) if (document.IsOpen)
...@@ -1269,6 +1315,7 @@ internal void StopPushingToWorkspaceHosts() ...@@ -1269,6 +1315,7 @@ internal void StopPushingToWorkspaceHosts()
internal void StartPushingToWorkspaceAndNotifyOfOpenDocuments() internal void StartPushingToWorkspaceAndNotifyOfOpenDocuments()
{ {
AssertIsForeground();
StartPushingToWorkspaceAndNotifyOfOpenDocuments(this); StartPushingToWorkspaceAndNotifyOfOpenDocuments(this);
} }
...@@ -1282,6 +1329,8 @@ internal bool PushingChangesToWorkspaceHosts ...@@ -1282,6 +1329,8 @@ internal bool PushingChangesToWorkspaceHosts
protected void UpdateRuleSetError(IRuleSetFile ruleSetFile) protected void UpdateRuleSetError(IRuleSetFile ruleSetFile)
{ {
AssertIsForeground();
if (this.HostDiagnosticUpdateSource == null) if (this.HostDiagnosticUpdateSource == null)
{ {
return; return;
...@@ -1304,6 +1353,8 @@ protected void UpdateRuleSetError(IRuleSetFile ruleSetFile) ...@@ -1304,6 +1353,8 @@ protected void UpdateRuleSetError(IRuleSetFile ruleSetFile)
protected void SetObjOutputPathAndRelatedData(string objOutputPath) protected void SetObjOutputPathAndRelatedData(string objOutputPath)
{ {
AssertIsForeground();
var currentObjOutputPath = this.ObjOutputPath; var currentObjOutputPath = this.ObjOutputPath;
if (PathUtilities.IsAbsolute(objOutputPath) && !string.Equals(currentObjOutputPath, objOutputPath, StringComparison.OrdinalIgnoreCase)) if (PathUtilities.IsAbsolute(objOutputPath) && !string.Equals(currentObjOutputPath, objOutputPath, StringComparison.OrdinalIgnoreCase))
{ {
...@@ -1332,6 +1383,8 @@ protected void SetObjOutputPathAndRelatedData(string objOutputPath) ...@@ -1332,6 +1383,8 @@ protected void SetObjOutputPathAndRelatedData(string objOutputPath)
private void UpdateAssemblyName() private void UpdateAssemblyName()
{ {
AssertIsForeground();
// set assembly name if changed // set assembly name if changed
// we use designTimeOutputPath to get assembly name since it is more reliable way to get the assembly name. // we use designTimeOutputPath to get assembly name since it is more reliable way to get the assembly name.
// otherwise, friend assembly all get messed up. // otherwise, friend assembly all get messed up.
...@@ -1349,6 +1402,8 @@ private void UpdateAssemblyName() ...@@ -1349,6 +1402,8 @@ private void UpdateAssemblyName()
protected void SetBinOutputPathAndRelatedData(string binOutputPath) protected void SetBinOutputPathAndRelatedData(string binOutputPath)
{ {
AssertIsForeground();
// refresh final output path // refresh final output path
var currentBinOutputPath = this.BinOutputPath; var currentBinOutputPath = this.BinOutputPath;
if (binOutputPath != null && !string.Equals(currentBinOutputPath, binOutputPath, StringComparison.OrdinalIgnoreCase)) if (binOutputPath != null && !string.Equals(currentBinOutputPath, binOutputPath, StringComparison.OrdinalIgnoreCase))
...@@ -1375,6 +1430,8 @@ protected void UpdateProjectFilePath(string newFilePath) ...@@ -1375,6 +1430,8 @@ protected void UpdateProjectFilePath(string newFilePath)
protected void UpdateProjectDisplayNameAndFilePath(string newDisplayName, string newFilePath) protected void UpdateProjectDisplayNameAndFilePath(string newDisplayName, string newFilePath)
{ {
AssertIsForeground();
bool updateMade = false; bool updateMade = false;
if (newDisplayName != null && this.DisplayName != newDisplayName) if (newDisplayName != null && this.DisplayName != newDisplayName)
...@@ -1398,6 +1455,8 @@ protected void UpdateProjectDisplayNameAndFilePath(string newDisplayName, string ...@@ -1398,6 +1455,8 @@ protected void UpdateProjectDisplayNameAndFilePath(string newDisplayName, string
private static void StartPushingToWorkspaceAndNotifyOfOpenDocuments(AbstractProject project) private static void StartPushingToWorkspaceAndNotifyOfOpenDocuments(AbstractProject project)
{ {
project.AssertIsForeground();
// If a document is opened in a project but we haven't started pushing yet, we want to stop doing lazy // If a document is opened in a project but we haven't started pushing yet, we want to stop doing lazy
// loading for this project and get it up to date so the user gets a fast experience there. If the file // loading for this project and get it up to date so the user gets a fast experience there. If the file
// was presented as open to us right away, then we'll never do this in OnDocumentOpened, so we should do // was presented as open to us right away, then we'll never do this in OnDocumentOpened, so we should do
...@@ -1462,6 +1521,8 @@ protected static bool FilterException(Exception e) ...@@ -1462,6 +1521,8 @@ protected static bool FilterException(Exception e)
public IReadOnlyList<string> GetFolderNamesFromHierarchy(uint documentItemID) public IReadOnlyList<string> GetFolderNamesFromHierarchy(uint documentItemID)
{ {
AssertIsForeground();
if (documentItemID != (uint)VSConstants.VSITEMID.Nil && Hierarchy.GetProperty(documentItemID, (int)VsHierarchyPropID.Parent, out var parentObj) == VSConstants.S_OK) if (documentItemID != (uint)VSConstants.VSITEMID.Nil && Hierarchy.GetProperty(documentItemID, (int)VsHierarchyPropID.Parent, out var parentObj) == VSConstants.S_OK)
{ {
var parentID = UnboxVSItemId(parentObj); var parentID = UnboxVSItemId(parentObj);
...@@ -1476,6 +1537,8 @@ public IReadOnlyList<string> GetFolderNamesFromHierarchy(uint documentItemID) ...@@ -1476,6 +1537,8 @@ public IReadOnlyList<string> GetFolderNamesFromHierarchy(uint documentItemID)
private IReadOnlyList<string> GetFolderNamesForFolder(uint folderItemID) private IReadOnlyList<string> GetFolderNamesForFolder(uint folderItemID)
{ {
AssertIsForeground();
// note: use of tmpFolders is assuming this API is called on UI thread only. // note: use of tmpFolders is assuming this API is called on UI thread only.
_tmpFolders.Clear(); _tmpFolders.Clear();
if (!_folderNameMap.TryGetValue(folderItemID, out var names)) if (!_folderNameMap.TryGetValue(folderItemID, out var names))
......
...@@ -21,6 +21,8 @@ internal partial class AbstractProject ...@@ -21,6 +21,8 @@ internal partial class AbstractProject
public void AddAnalyzerReference(string analyzerAssemblyFullPath) public void AddAnalyzerReference(string analyzerAssemblyFullPath)
{ {
AssertIsForeground();
if (CurrentProjectAnalyzersContains(analyzerAssemblyFullPath)) if (CurrentProjectAnalyzersContains(analyzerAssemblyFullPath))
{ {
return; return;
...@@ -70,6 +72,8 @@ public void AddAnalyzerReference(string analyzerAssemblyFullPath) ...@@ -70,6 +72,8 @@ public void AddAnalyzerReference(string analyzerAssemblyFullPath)
public void RemoveAnalyzerReference(string analyzerAssemblyFullPath) public void RemoveAnalyzerReference(string analyzerAssemblyFullPath)
{ {
AssertIsForeground();
if (!TryGetAnalyzer(analyzerAssemblyFullPath, out var analyzer)) if (!TryGetAnalyzer(analyzerAssemblyFullPath, out var analyzer))
{ {
return; return;
...@@ -100,6 +104,8 @@ public void RemoveAnalyzerReference(string analyzerAssemblyFullPath) ...@@ -100,6 +104,8 @@ public void RemoveAnalyzerReference(string analyzerAssemblyFullPath)
public void SetRuleSetFile(string ruleSetFileFullPath) public void SetRuleSetFile(string ruleSetFileFullPath)
{ {
AssertIsForeground();
if (ruleSetFileFullPath == null) if (ruleSetFileFullPath == null)
{ {
ruleSetFileFullPath = string.Empty; ruleSetFileFullPath = string.Empty;
...@@ -123,6 +129,8 @@ public void SetRuleSetFile(string ruleSetFileFullPath) ...@@ -123,6 +129,8 @@ public void SetRuleSetFile(string ruleSetFileFullPath)
public void AddAdditionalFile(string additionalFilePath, Func<IVisualStudioHostDocument, bool> getIsInCurrentContext) public void AddAdditionalFile(string additionalFilePath, Func<IVisualStudioHostDocument, bool> getIsInCurrentContext)
{ {
AssertIsForeground();
var document = this.DocumentProvider.TryGetDocumentForFile( var document = this.DocumentProvider.TryGetDocumentForFile(
this, this,
filePath: additionalFilePath, filePath: additionalFilePath,
...@@ -180,6 +188,8 @@ private void ClearAnalyzerRuleSet() ...@@ -180,6 +188,8 @@ private void ClearAnalyzerRuleSet()
// internal for testing purpose. // internal for testing purpose.
internal void OnRuleSetFileUpdateOnDisk(object sender, EventArgs e) internal void OnRuleSetFileUpdateOnDisk(object sender, EventArgs e)
{ {
AssertIsForeground();
var filePath = this.RuleSetFile.FilePath; var filePath = this.RuleSetFile.FilePath;
ResetAnalyzerRuleSet(filePath); ResetAnalyzerRuleSet(filePath);
......
...@@ -51,6 +51,8 @@ private void SetArgumentsCore(string commandLine, CommandLineArguments commandLi ...@@ -51,6 +51,8 @@ private void SetArgumentsCore(string commandLine, CommandLineArguments commandLi
/// </summary> /// </summary>
protected void UpdateOptions() protected void UpdateOptions()
{ {
AssertIsForeground();
CommandLineArguments lastParsedCommandLineArguments = _lastParsedCommandLineArguments; CommandLineArguments lastParsedCommandLineArguments = _lastParsedCommandLineArguments;
Contract.ThrowIfNull(lastParsedCommandLineArguments); Contract.ThrowIfNull(lastParsedCommandLineArguments);
...@@ -70,6 +72,8 @@ protected void UpdateOptions() ...@@ -70,6 +72,8 @@ protected void UpdateOptions()
/// </summary> /// </summary>
protected CommandLineArguments SetArgumentsAndUpdateOptions(string commandLine) protected CommandLineArguments SetArgumentsAndUpdateOptions(string commandLine)
{ {
AssertIsForeground();
var commandLineArguments = SetArguments(commandLine); var commandLineArguments = SetArguments(commandLine);
UpdateOptions(); UpdateOptions();
return commandLineArguments; return commandLineArguments;
...@@ -109,6 +113,8 @@ protected CommandLineArguments SetArguments(string commandLine) ...@@ -109,6 +113,8 @@ protected CommandLineArguments SetArguments(string commandLine)
/// </summary> /// </summary>
protected void SetOptions(CompilationOptions newCompilationOptions, ParseOptions newParseOptions) protected void SetOptions(CompilationOptions newCompilationOptions, ParseOptions newParseOptions)
{ {
AssertIsForeground();
this.UpdateRuleSetError(this.RuleSetFile); this.UpdateRuleSetError(this.RuleSetFile);
// Set options. // Set options.
......
...@@ -65,16 +65,9 @@ internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffin ...@@ -65,16 +65,9 @@ internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffin
/// </summary> /// </summary>
private readonly Dictionary<string, ImmutableArray<AbstractProject>> _projectsByBinPath = new Dictionary<string, ImmutableArray<AbstractProject>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ImmutableArray<AbstractProject>> _projectsByBinPath = new Dictionary<string, ImmutableArray<AbstractProject>>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Holds the task with continuations to sequentially execute all the foreground affinitized actions on the foreground task scheduler.
/// More specifically, all the notifications to workspace hosts are executed on the foreground thread. However, the project system might make project state changes
/// and request notifications to workspace hosts on background thread. So we queue up all the notifications for project state changes onto this task and execute them on the foreground thread.
/// </summary>
private Task _taskForForegroundAffinitizedActions = Task.CompletedTask;
private readonly Dictionary<ProjectId, AbstractProject> _projectMap; private readonly Dictionary<ProjectId, AbstractProject> _projectMap;
private readonly Dictionary<string, ProjectId> _projectPathToIdMap; private readonly Dictionary<string, ProjectId> _projectPathToIdMap;
#endregion #endregion
/// <summary> /// <summary>
/// Provided to not break CodeLens which has a dependency on this API until there is a /// Provided to not break CodeLens which has a dependency on this API until there is a
...@@ -133,35 +126,6 @@ public VisualStudioProjectTracker(IServiceProvider serviceProvider, HostWorkspac ...@@ -133,35 +126,6 @@ public VisualStudioProjectTracker(IServiceProvider serviceProvider, HostWorkspac
} }
} }
private void ScheduleForegroundAffinitizedAction(Action action)
{
AssertIsBackground();
lock (_gate)
{
_taskForForegroundAffinitizedActions = _taskForForegroundAffinitizedActions.SafeContinueWith(_ => action(), ForegroundTaskScheduler);
}
}
/// <summary>
/// If invoked on the foreground thread, the action is executed right away.
/// Otherwise, the action is scheduled on foreground task scheduler.
/// </summary>
/// <param name="action">Action that needs to be executed on a foreground thread.</param>
private void ExecuteOrScheduleForegroundAffinitizedAction(Action action)
{
if (IsForeground())
{
// We are already on the foreground thread, execute the given action.
action();
}
else
{
// Schedule the update on the foreground task scheduler.
ScheduleForegroundAffinitizedAction(action);
}
}
public void RegisterSolutionProperties(SolutionId solutionId) public void RegisterSolutionProperties(SolutionId solutionId)
{ {
AssertIsForeground(); AssertIsForeground();
...@@ -204,12 +168,6 @@ public string GetWorkingFolderPath(Solution solution) ...@@ -204,12 +168,6 @@ public string GetWorkingFolderPath(Solution solution)
} }
public void RegisterWorkspaceHost(IVisualStudioWorkspaceHost host) public void RegisterWorkspaceHost(IVisualStudioWorkspaceHost host)
{
ExecuteOrScheduleForegroundAffinitizedAction(
() => RegisterWorkspaceHostOnForeground(host));
}
private void RegisterWorkspaceHostOnForeground(IVisualStudioWorkspaceHost host)
{ {
this.AssertIsForeground(); this.AssertIsForeground();
...@@ -298,16 +256,9 @@ internal bool ContainsProject(AbstractProject project) ...@@ -298,16 +256,9 @@ internal bool ContainsProject(AbstractProject project)
/// <summary> /// <summary>
/// Add a project to the workspace. /// Add a project to the workspace.
/// If invoked on the foreground thread, the add is executed right away.
/// Otherwise, the add is scheduled on foreground task scheduler.
/// </summary> /// </summary>
/// <remarks>This method may be called on a background thread.</remarks> /// <remarks>This method must be called on the foreground thread.</remarks>
internal void AddProject(AbstractProject project) internal void AddProject(AbstractProject project)
{
ExecuteOrScheduleForegroundAffinitizedAction(() => AddProject_Foreground(project));
}
private void AddProject_Foreground(AbstractProject project)
{ {
AssertIsForeground(); AssertIsForeground();
...@@ -317,11 +268,11 @@ private void AddProject_Foreground(AbstractProject project) ...@@ -317,11 +268,11 @@ private void AddProject_Foreground(AbstractProject project)
} }
// UpdateProjectBinPath is defensively executed on the foreground thread as it calls back into referencing projects to perform metadata to P2P reference conversions. // UpdateProjectBinPath is defensively executed on the foreground thread as it calls back into referencing projects to perform metadata to P2P reference conversions.
UpdateProjectBinPath_Foreground(project, null, project.BinOutputPath); UpdateProjectBinPath(project, null, project.BinOutputPath);
if (_solutionLoadComplete) if (_solutionLoadComplete)
{ {
StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground(SpecializedCollections.SingletonEnumerable(project)); StartPushingToWorkspaceAndNotifyOfOpenDocuments(SpecializedCollections.SingletonEnumerable(project));
} }
else else
{ {
...@@ -331,25 +282,12 @@ private void AddProject_Foreground(AbstractProject project) ...@@ -331,25 +282,12 @@ private void AddProject_Foreground(AbstractProject project)
/// <summary> /// <summary>
/// Starts pushing events from the given projects to the workspace hosts and notifies about open documents. /// Starts pushing events from the given projects to the workspace hosts and notifies about open documents.
/// If invoked on the foreground thread, it is executed right away.
/// Otherwise, it is scheduled on foreground task scheduler.
/// </summary> /// </summary>
/// <remarks>This method may be called on a background thread.</remarks> /// <remarks>This method must be called on the foreground thread.</remarks>
internal void StartPushingToWorkspaceAndNotifyOfOpenDocuments(IEnumerable<AbstractProject> projects) internal void StartPushingToWorkspaceAndNotifyOfOpenDocuments(IEnumerable<AbstractProject> projects)
{
ExecuteOrScheduleForegroundAffinitizedAction(() => StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground(projects));
}
private void StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground(IEnumerable<AbstractProject> projects)
{ {
AssertIsForeground(); AssertIsForeground();
// StartPushingToWorkspaceAndNotifyOfOpenDocuments might be invoked from a background thread,
// and hence StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground scheduled to be executed later on the foreground task scheduler.
// By the time it gets scheduled, we might have removed some project(s) from the tracker on the UI thread.
// So, we filter out the projects that have been removed from the tracker.
projects = projects.Where(p => this.ContainsProject(p));
using (Dispatcher.CurrentDispatcher.DisableProcessing()) using (Dispatcher.CurrentDispatcher.DisableProcessing())
{ {
foreach (var hostState in _workspaceHosts) foreach (var hostState in _workspaceHosts)
...@@ -363,14 +301,6 @@ private void StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground(IEnumera ...@@ -363,14 +301,6 @@ private void StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground(IEnumera
/// Remove a project from the workspace. /// Remove a project from the workspace.
/// </summary> /// </summary>
internal void RemoveProject(AbstractProject project) internal void RemoveProject(AbstractProject project)
{
ExecuteOrScheduleForegroundAffinitizedAction(() => RemoveProject_Foreground(project));
}
/// <summary>
/// Remove a project from the workspace.
/// </summary>
private void RemoveProject_Foreground(AbstractProject project)
{ {
AssertIsForeground(); AssertIsForeground();
...@@ -379,7 +309,7 @@ private void RemoveProject_Foreground(AbstractProject project) ...@@ -379,7 +309,7 @@ private void RemoveProject_Foreground(AbstractProject project)
Contract.ThrowIfFalse(_projectMap.Remove(project.Id)); Contract.ThrowIfFalse(_projectMap.Remove(project.Id));
} }
UpdateProjectBinPath_Foreground(project, project.BinOutputPath, null); UpdateProjectBinPath(project, project.BinOutputPath, null);
using (Dispatcher.CurrentDispatcher.DisableProcessing()) using (Dispatcher.CurrentDispatcher.DisableProcessing())
{ {
...@@ -392,16 +322,8 @@ private void RemoveProject_Foreground(AbstractProject project) ...@@ -392,16 +322,8 @@ private void RemoveProject_Foreground(AbstractProject project)
/// <summary> /// <summary>
/// Updates the project tracker and referencing projects for binary output path change for the given project. /// Updates the project tracker and referencing projects for binary output path change for the given project.
/// If invoked on the foreground thread, the update is executed right away.
/// Otherwise, update is scheduled on foreground task scheduler.
/// </summary> /// </summary>
/// <remarks>This method may be called on a background thread.</remarks>
internal void UpdateProjectBinPath(AbstractProject project, string oldBinPathOpt, string newBinPathOpt) internal void UpdateProjectBinPath(AbstractProject project, string oldBinPathOpt, string newBinPathOpt)
{
ExecuteOrScheduleForegroundAffinitizedAction(() => UpdateProjectBinPath_Foreground(project, oldBinPathOpt, newBinPathOpt));
}
internal void UpdateProjectBinPath_Foreground(AbstractProject project, string oldBinPathOpt, string newBinPathOpt)
{ {
// UpdateProjectBinPath is defensively executed on the foreground thread as it calls back into referencing projects to perform metadata to P2P reference conversions. // UpdateProjectBinPath is defensively executed on the foreground thread as it calls back into referencing projects to perform metadata to P2P reference conversions.
AssertIsForeground(); AssertIsForeground();
...@@ -473,16 +395,9 @@ internal ProjectId GetOrCreateProjectIdForPath(string projectPath, string projec ...@@ -473,16 +395,9 @@ internal ProjectId GetOrCreateProjectIdForPath(string projectPath, string projec
/// <summary> /// <summary>
/// Notifies the workspace host about the given action. /// Notifies the workspace host about the given action.
/// If invoked on the foreground thread, the action is executed right away.
/// Otherwise, the action is scheduled on foreground task scheduler.
/// </summary> /// </summary>
/// <remarks>This method may be called on a background thread.</remarks> /// <remarks>This method must be called on the foreground thread.</remarks>
internal void NotifyWorkspaceHosts(Action<IVisualStudioWorkspaceHost> action) internal void NotifyWorkspaceHosts(Action<IVisualStudioWorkspaceHost> action)
{
ExecuteOrScheduleForegroundAffinitizedAction(() => NotifyWorkspaceHosts_Foreground(action));
}
internal void NotifyWorkspaceHosts_Foreground(Action<IVisualStudioWorkspaceHost> action)
{ {
AssertIsForeground(); AssertIsForeground();
......
...@@ -97,8 +97,8 @@ int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved) ...@@ -97,8 +97,8 @@ int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved)
Contract.ThrowIfFalse(_projectMap.Count == 0); Contract.ThrowIfFalse(_projectMap.Count == 0);
} }
NotifyWorkspaceHosts_Foreground(host => host.OnSolutionRemoved()); NotifyWorkspaceHosts(host => host.OnSolutionRemoved());
NotifyWorkspaceHosts_Foreground(host => host.ClearSolution()); NotifyWorkspaceHosts(host => host.ClearSolution());
lock (_gate) lock (_gate)
{ {
......
...@@ -374,7 +374,7 @@ private static string GetLanguageOfProject(string projectFilename) ...@@ -374,7 +374,7 @@ private static string GetLanguageOfProject(string projectFilename)
private void FinishLoad() private void FinishLoad()
{ {
// We are now completely done, so let's simply ensure all projects are added. // We are now completely done, so let's simply ensure all projects are added.
StartPushingToWorkspaceAndNotifyOfOpenDocuments_Foreground(this.ImmutableProjects); StartPushingToWorkspaceAndNotifyOfOpenDocuments(this.ImmutableProjects);
// Also, all remaining project adds need to immediately pushed as well, since we're now "interactive" // Also, all remaining project adds need to immediately pushed as well, since we're now "interactive"
_solutionLoadComplete = true; _solutionLoadComplete = true;
......
...@@ -58,9 +58,14 @@ private void NormalizeAndSetBinOutputPathAndRelatedData(string binOutputPath) ...@@ -58,9 +58,14 @@ private void NormalizeAndSetBinOutputPathAndRelatedData(string binOutputPath)
SetBinOutputPathAndRelatedData(binOutputPath); SetBinOutputPathAndRelatedData(binOutputPath);
} }
private bool _disconnected;
// We might we invoked from a background thread, so schedule the disconnect on foreground task scheduler. // We might we invoked from a background thread, so schedule the disconnect on foreground task scheduler.
public sealed override void Disconnect() public sealed override void Disconnect()
{ {
_disconnected = true;
if (IsForeground()) if (IsForeground())
{ {
DisconnectCore(); DisconnectCore();
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.LanguageServices.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.ProjectSystem;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -11,6 +13,43 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.C ...@@ -11,6 +13,43 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.C
{ {
internal sealed partial class CPSProject : AbstractProject, IWorkspaceProjectContext internal sealed partial class CPSProject : AbstractProject, IWorkspaceProjectContext
{ {
/// <summary>
/// Holds the task with continuations to sequentially execute all the foreground affinitized actions on the foreground task scheduler.
/// More specifically, all the notifications to workspace hosts are executed on the foreground thread. However, the project system might make project state changes
/// and request notifications to workspace hosts on background thread. So we queue up all the notifications for project state changes onto this task and execute them on the foreground thread.
/// </summary>
private Task _foregroundTaskQueue = Task.CompletedTask;
/// <summary>
/// Controls access to task queue
/// </summary>
private readonly object _queueGate = new object();
private void ExecuteForegroundAction(Action action)
{
if (IsForeground())
{
action();
}
else
{
lock (_queueGate)
{
_foregroundTaskQueue = _foregroundTaskQueue.SafeContinueWith(
_ =>
{
// since execution is now technically asynchronous
// only execute action if project is not disconnected and currently being tracked.
if (!_disconnected && this.ProjectTracker.ContainsProject(this))
{
action();
}
},
ForegroundTaskScheduler);
}
}
}
#region Project properties #region Project properties
string IWorkspaceProjectContext.DisplayName string IWorkspaceProjectContext.DisplayName
{ {
...@@ -78,45 +117,63 @@ string IWorkspaceProjectContext.BinOutputPath ...@@ -78,45 +117,63 @@ string IWorkspaceProjectContext.BinOutputPath
#region Options #region Options
public void SetOptions(string commandLineForOptions) public void SetOptions(string commandLineForOptions)
{ {
var commandLineArguments = SetArgumentsAndUpdateOptions(commandLineForOptions); ExecuteForegroundAction(() =>
PostSetOptions(commandLineArguments); {
var commandLineArguments = SetArgumentsAndUpdateOptions(commandLineForOptions);
PostSetOptions(commandLineArguments);
});
} }
private void PostSetOptions(CommandLineArguments commandLineArguments) private void PostSetOptions(CommandLineArguments commandLineArguments)
{ {
// Invoke SetOutputPathAndRelatedData to update the project obj output path. ExecuteForegroundAction(() =>
if (commandLineArguments.OutputFileName != null && commandLineArguments.OutputDirectory != null)
{ {
var objOutputPath = PathUtilities.CombinePathsUnchecked(commandLineArguments.OutputDirectory, commandLineArguments.OutputFileName); // Invoke SetOutputPathAndRelatedData to update the project obj output path.
SetObjOutputPathAndRelatedData(objOutputPath); if (commandLineArguments.OutputFileName != null && commandLineArguments.OutputDirectory != null)
} {
var objOutputPath = PathUtilities.CombinePathsUnchecked(commandLineArguments.OutputDirectory, commandLineArguments.OutputFileName);
SetObjOutputPathAndRelatedData(objOutputPath);
}
});
} }
#endregion #endregion
#region References #region References
public void AddMetadataReference(string referencePath, MetadataReferenceProperties properties) public void AddMetadataReference(string referencePath, MetadataReferenceProperties properties)
{ {
referencePath = FileUtilities.NormalizeAbsolutePath(referencePath); ExecuteForegroundAction(() =>
AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(referencePath, properties); {
referencePath = FileUtilities.NormalizeAbsolutePath(referencePath);
AddMetadataReferenceAndTryConvertingToProjectReferenceIfPossible(referencePath, properties);
});
} }
public new void RemoveMetadataReference(string referencePath) public new void RemoveMetadataReference(string referencePath)
{ {
referencePath = FileUtilities.NormalizeAbsolutePath(referencePath); ExecuteForegroundAction(() =>
base.RemoveMetadataReference(referencePath); {
referencePath = FileUtilities.NormalizeAbsolutePath(referencePath);
base.RemoveMetadataReference(referencePath);
});
} }
public void AddProjectReference(IWorkspaceProjectContext project, MetadataReferenceProperties properties) public void AddProjectReference(IWorkspaceProjectContext project, MetadataReferenceProperties properties)
{ {
var abstractProject = GetAbstractProject(project); ExecuteForegroundAction(() =>
AddProjectReference(new ProjectReference(abstractProject.Id, properties.Aliases, properties.EmbedInteropTypes)); {
var abstractProject = GetAbstractProject(project);
AddProjectReference(new ProjectReference(abstractProject.Id, properties.Aliases, properties.EmbedInteropTypes));
});
} }
public void RemoveProjectReference(IWorkspaceProjectContext project) public void RemoveProjectReference(IWorkspaceProjectContext project)
{ {
var referencedProject = GetAbstractProject(project); ExecuteForegroundAction(() =>
var projectReference = GetCurrentProjectReferences().Single(p => p.ProjectId == referencedProject.Id); {
RemoveProjectReference(projectReference); var referencedProject = GetAbstractProject(project);
var projectReference = GetCurrentProjectReferences().Single(p => p.ProjectId == referencedProject.Id);
RemoveProjectReference(projectReference);
});
} }
private AbstractProject GetAbstractProject(IWorkspaceProjectContext project) private AbstractProject GetAbstractProject(IWorkspaceProjectContext project)
...@@ -134,17 +191,26 @@ private AbstractProject GetAbstractProject(IWorkspaceProjectContext project) ...@@ -134,17 +191,26 @@ private AbstractProject GetAbstractProject(IWorkspaceProjectContext project)
#region Files #region Files
public void AddSourceFile(string filePath, bool isInCurrentContext = true, IEnumerable<string> folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular) public void AddSourceFile(string filePath, bool isInCurrentContext = true, IEnumerable<string> folderNames = null, SourceCodeKind sourceCodeKind = SourceCodeKind.Regular)
{ {
AddFile(filePath, sourceCodeKind, _ => isInCurrentContext, _ => folderNames.ToImmutableArrayOrEmpty()); ExecuteForegroundAction(() =>
{
AddFile(filePath, sourceCodeKind, _ => isInCurrentContext, _ => folderNames.ToImmutableArrayOrEmpty());
});
} }
public void RemoveSourceFile(string filePath) public void RemoveSourceFile(string filePath)
{ {
RemoveFile(filePath); ExecuteForegroundAction(() =>
{
RemoveFile(filePath);
});
} }
public void AddAdditionalFile(string filePath, bool isInCurrentContext = true) public void AddAdditionalFile(string filePath, bool isInCurrentContext = true)
{ {
AddAdditionalFile(filePath, getIsInCurrentContext: _ => isInCurrentContext); ExecuteForegroundAction(() =>
{
AddAdditionalFile(filePath, getIsInCurrentContext: _ => isInCurrentContext);
});
} }
#endregion #endregion
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册