The problem is in, where we read CurrentSolution, update it and set it back without taking _serializationLock
internal void UpdateCurrentSolutionOnOptionsChanged()
{
var newOptions = _optionService.GetSerializableOptionsSnapshot(this.CurrentSolution.State.GetProjectLanguages());
this.SetCurrentSolution(this.CurrentSolution.WithOptions(newOptions));
}
Simply taking the lock, however, leads to a deadlock with stack trace. Method frames marked by > would take the lock.
Microsoft.CodeAnalysis.Workspaces.dll!Roslyn.Utilities.SemaphoreSlimExtensions.DisposableWait(System.Threading.SemaphoreSlim semaphore, System.Threading.CancellationToken cancellationToken) Line 17 C#
> Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.UpdateCurrentSolutionOnOptionsChanged() Line 281 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Options.GlobalOptionService.UpdateRegisteredWorkspacesAndRaiseEvents(System.Collections.Generic.List<Microsoft.CodeAnalysis.Options.OptionChangedEventArgs> changedOptions) Line 364 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Options.GlobalOptionService.SetOptions(Microsoft.CodeAnalysis.Options.OptionSet optionSet) Line 325 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Options.OptionServiceFactory.OptionService.SetOptions(Microsoft.CodeAnalysis.Options.OptionSet optionSet) Line 128 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.SetOptions(Microsoft.CodeAnalysis.Options.OptionSet options) Line 276 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.TryApplyChanges(Microsoft.CodeAnalysis.Solution newSolution, Microsoft.CodeAnalysis.Shared.Utilities.IProgressTracker progressTracker) Line 1260 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.TryApplyChanges(Microsoft.CodeAnalysis.Solution newSolution) Line 1199 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.DiagnosticProvider.Enable(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DiagnosticProvider.Options options) Line 21 C#
Microsoft.CodeAnalysis.Features.dll!Microsoft.CodeAnalysis.SolutionCrawler.MiscSolutionCrawlerWorkspaceEventListener.StartListening(Microsoft.CodeAnalysis.Workspace workspace, object serviceOpt) Line 23 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Host.DefaultWorkspaceEventListenerServiceFactory.Service.EnsureListeners() Line 74 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.GetEventHandlers<Microsoft.CodeAnalysis.WorkspaceChangeEventArgs>(string eventName) Line 216 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.RaiseWorkspaceChangedEventAsync(Microsoft.CodeAnalysis.WorkspaceChangeKind kind, Microsoft.CodeAnalysis.Solution oldSolution, Microsoft.CodeAnalysis.Solution newSolution, Microsoft.CodeAnalysis.ProjectId projectId, …
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.OnProjectAdded_NoLock(Microsoft.CodeAnalysis.ProjectInfo projectInfo, bool silent) Line 484 C#
> Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.OnProjectAdded(Microsoft.CodeAnalysis.ProjectInfo projectInfo, bool silent) Line 469 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.OnProjectAdded(Microsoft.CodeAnalysis.ProjectInfo projectInfo) Line 462 C#
another deadlock once the one above is addressed:
> Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.UpdateCurrentSolutionOnOptionsChanged() Line 237 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Options.GlobalOptionService.UpdateRegisteredWorkspacesAndRaiseEvents(System.Collections.Generic.List<Microsoft.CodeAnalysis.Options.OptionChangedEventArgs> changedOptions) Line 364 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Options.GlobalOptionService.SetOptions(Microsoft.CodeAnalysis.Options.OptionSet optionSet) Line 325 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Options.OptionServiceFactory.OptionService.SetOptions(Microsoft.CodeAnalysis.Options.OptionSet optionSet) Line 128 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.SetOptions(Microsoft.CodeAnalysis.Options.OptionSet options) Line 231 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.TryApplyChanges(Microsoft.CodeAnalysis.Solution newSolution, Microsoft.CodeAnalysis.Shared.Utilities.IProgressTracker progressTracker) Line 1255 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.TryApplyChanges(Microsoft.CodeAnalysis.Solution newSolution) Line 1194 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.DiagnosticProvider.Enable(Microsoft.CodeAnalysis.Workspace workspace, Microsoft.CodeAnalysis.DiagnosticProvider.Options options) Line 21 C#
Microsoft.CodeAnalysis.Features.dll!Microsoft.CodeAnalysis.SolutionCrawler.MiscSolutionCrawlerWorkspaceEventListener.StartListening(Microsoft.CodeAnalysis.Workspace workspace, object serviceOpt) Line 23 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Host.DefaultWorkspaceEventListenerServiceFactory.Service.EnsureListeners() Line 74 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.GetEventHandlers<Microsoft.CodeAnalysis.WorkspaceChangeEventArgs>(string eventName) Line 216 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.RaiseWorkspaceChangedEventAsync(Microsoft.CodeAnalysis.WorkspaceChangeKind kind, Microsoft.CodeAnalysis.Solution oldSolution, Microsoft.CodeAnalysis.Solution newSolution, Microsoft.CodeAnalysis.ProjectId projectId, Microsoft.CodeAnalysis.DocumentId documentId) Line 58 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.OnProjectAdded(Microsoft.CodeAnalysis.ProjectInfo projectInfo) Line 443 C#
Microsoft.VisualStudio.LanguageServices.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.MiscellaneousFilesWorkspace.AttachToDocument(string moniker, Microsoft.VisualStudio.Text.ITextBuffer textBuffer) Line 262 C#
Microsoft.VisualStudio.LanguageServices.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.MiscellaneousFilesWorkspace.Registration_WorkspaceChanged(object sender, System.EventArgs e) Line 203 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.WorkspaceRegistration.RaiseEvents() Line 53 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.WorkspaceRegistration.SetWorkspaceAndRaiseEvents(Microsoft.CodeAnalysis.Workspace workspace) Line 26 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.UnregisterText(Microsoft.CodeAnalysis.Text.SourceTextContainer textContainer) Line 65 C#
Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.ClearOpenDocument(Microsoft.CodeAnalysis.DocumentId documentId) Line 100 C#
> Microsoft.CodeAnalysis.Workspaces.dll!Microsoft.CodeAnalysis.Workspace.OnDocumentClosed(Microsoft.CodeAnalysis.DocumentId documentId, Microsoft.CodeAnalysis.TextLoader reloader, bool updateActiveContext) Line 572 C#
Microsoft.VisualStudio.LanguageServices.dll!Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.VisualStudioWorkspaceImpl.OpenFileTracker.TryClosingDocumentsForMoniker.AnonymousMethod__0(Microsoft.CodeAnalysis.Workspace w) Line 317 C#
The solution is to avoid calling RaiseWorkspaceChangedEventAsync while the lock is taken. But we need to keep the events ordered in the same order the solution snapshots changed.
Also related:
#32551
The problem is in, where we read CurrentSolution, update it and set it back without taking
_serializationLockSimply taking the lock, however, leads to a deadlock with stack trace. Method frames marked by
>would take the lock.another deadlock once the one above is addressed:
The solution is to avoid calling
RaiseWorkspaceChangedEventAsyncwhile the lock is taken. But we need to keep the events ordered in the same order the solution snapshots changed.Also related:
#32551