Skip to content

Workspace.SetOptions has a race condition #42229

@tmat

Description

@tmat

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

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions