diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index df886377f94f3..1d2443fe74b50 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -254,6 +254,7 @@ private protected (bool updated, Solution newSolution) SetCurrentSolution( return UnifyLinkedDocumentContents(oldSolution, newSolution); }, + mayRaiseEvents: true, onBeforeUpdate: static (oldSolution, newSolution, data) => { data.onBeforeUpdate?.Invoke(oldSolution, newSolution); @@ -342,6 +343,10 @@ static Solution UpdateExistingDocumentsToChangedDocumentContents(Solution soluti /// Solution transformation. This may be run multiple times. As such it should be /// a purely functional transformation on the solution instance passed to it. It should not make stateful /// changes elsewhere. + /// if this operation may raise observable events; + /// otherwise, . If , the operation will call + /// to ensure listeners are registered prior to callbacks that may raise + /// events. /// Action to perform immediately prior to updating . /// The action will be passed the old that will be replaced and the exact solution /// it will be replaced with. The latter may be different than the solution returned by ( TData data, Func transformation, + bool mayRaiseEvents = true, Action? onBeforeUpdate = null, Action? onAfterUpdate = null) { @@ -363,6 +369,7 @@ private protected (Solution oldSolution, Solution newSolution) SetCurrentSolutio useAsync: false, data, transformation, + mayRaiseEvents, onBeforeUpdate, onAfterUpdate, CancellationToken.None); @@ -371,11 +378,12 @@ private protected (Solution oldSolution, Solution newSolution) SetCurrentSolutio #pragma warning restore CA2012 // Use ValueTasks correctly } - /// + /// private protected async ValueTask<(Solution oldSolution, Solution newSolution)> SetCurrentSolutionAsync( bool useAsync, TData data, Func transformation, + bool mayRaiseEvents, Action? onBeforeUpdate, Action? onAfterUpdate, CancellationToken cancellationToken) @@ -384,9 +392,12 @@ private protected (Solution oldSolution, Solution newSolution) SetCurrentSolutio var oldSolution = Volatile.Read(ref _latestSolution); - // Ensure our event handlers are realized prior to taking this lock. We don't want to deadlock trying - // to obtain them when calling one of our callbacks. See https://github.com/dotnet/roslyn/issues/64681 - EnsureEventListeners(); + if (mayRaiseEvents) + { + // Ensure our event handlers are realized prior to taking this lock. We don't want to deadlock trying + // to obtain them when calling one of our callbacks. See https://github.com/dotnet/roslyn/issues/64681 + EnsureEventListeners(); + } while (true) { @@ -499,6 +510,7 @@ private void ClearSolution(bool reportChangeEvent) this.SetCurrentSolution( data: /*unused*/ 0, (oldSolution, _) => this.CreateSolution(oldSolution.Id), + mayRaiseEvents: reportChangeEvent, onBeforeUpdate: (_, _, _) => this.ClearSolutionData(), onAfterUpdate: (oldSolution, newSolution, _) => {