@@ -156,66 +156,65 @@ public void ChainDisposal(IDisposable disposable)
156156 /// both evaluation and build updates may arrive in any order, so long as values
157157 /// of each type are ordered correctly.
158158 /// </para>
159- /// <para>
160- /// Calls must not overlap. This method is not thread-safe. This method is designed
161- /// to be called from a dataflow ActionBlock, which will serialize calls, so we
162- /// needn't perform any locking or protection here.
163- /// </para>
164159 /// </remarks>
165160 /// <param name="update">The project update to integrate.</param>
166161 /// <returns>A task that completes when the update has been integrated.</returns>
167- internal async Task OnWorkspaceUpdateAsync ( IProjectVersionedValue < WorkspaceUpdate > update )
162+ internal Task OnWorkspaceUpdateAsync ( IProjectVersionedValue < WorkspaceUpdate > update )
168163 {
169- Verify . NotDisposed ( this ) ;
164+ // Prevent disposal during the update.
165+ return ExecuteUnderLockAsync ( async token =>
166+ {
167+ Verify . NotDisposed ( this ) ;
168+
169+ await InitializeAsync ( _unloadCancellationToken ) ;
170170
171- await InitializeAsync ( _unloadCancellationToken ) ;
171+ Assumes . True ( _state is WorkspaceState . Uninitialized or WorkspaceState . Initialized ) ;
172172
173- Assumes . True ( _state is WorkspaceState . Uninitialized or WorkspaceState . Initialized ) ;
173+ await _joinableTaskFactory . RunAsync ( ApplyUpdateWithinLockAsync ) ;
174+ } ) ;
174175
175- await _joinableTaskFactory . RunAsync (
176- async ( ) =>
176+ async Task ApplyUpdateWithinLockAsync ( )
177+ {
178+ // We will always receive an evaluation update before the first build update.
179+
180+ if ( TryTransition ( WorkspaceState . Uninitialized , WorkspaceState . Initialized ) )
177181 {
178- // Calls never overlap. No synchronisation is needed here.
179- // We can receive either evaluation OR build data first.
182+ // Note that we create operation progress registrations using the first primary (active) configuration
183+ // within the slice. Over time this may change, but we keep the same registration to the first seen .
180184
181- if ( TryTransition ( WorkspaceState . Uninitialized , WorkspaceState . Initialized ) )
185+ ConfiguredProject configuredProject = update . Value switch
182186 {
183- // Note that we create operation progress registrations using the first primary (active) configuration
184- // within the slice. Over time this may change, but we keep the same registration to the first seen.
185-
186- ConfiguredProject configuredProject = update . Value switch
187- {
188- { EvaluationUpdate : EvaluationUpdate update } => update . ConfiguredProject ,
189- { BuildUpdate : BuildUpdate update } => update . ConfiguredProject ,
190- _ => throw Assumes . NotReachable ( )
191- } ;
187+ { EvaluationUpdate : EvaluationUpdate update } => update . ConfiguredProject ,
188+ { BuildUpdate : BuildUpdate update } => update . ConfiguredProject ,
189+ _ => throw Assumes . NotReachable ( )
190+ } ;
192191
193- _evaluationProgressRegistration = _dataProgressTrackerService . RegisterForIntelliSense ( this , configuredProject , "LanguageServiceHost.Workspace.Evaluation" ) ;
194- _buildProgressRegistration = _dataProgressTrackerService . RegisterForIntelliSense ( this , configuredProject , "LanguageServiceHost.Workspace.ProjectBuild" ) ;
192+ _evaluationProgressRegistration = _dataProgressTrackerService . RegisterForIntelliSense ( this , configuredProject , "LanguageServiceHost.Workspace.Evaluation" ) ;
193+ _buildProgressRegistration = _dataProgressTrackerService . RegisterForIntelliSense ( this , configuredProject , "LanguageServiceHost.Workspace.ProjectBuild" ) ;
195194
196- _disposableBag . Add ( _evaluationProgressRegistration ) ;
197- _disposableBag . Add ( _buildProgressRegistration ) ;
198- }
195+ _disposableBag . Add ( _evaluationProgressRegistration ) ;
196+ _disposableBag . Add ( _buildProgressRegistration ) ;
197+ }
199198
200- try
201- {
202- await ( update . Value switch
203- {
204- { EvaluationUpdate : not null } => OnEvaluationUpdateAsync ( update . Derive ( u => u . EvaluationUpdate ! ) ) ,
205- { BuildUpdate : not null } => OnBuildUpdateAsync ( update . Derive ( u => u . BuildUpdate ! ) ) ,
206- _ => throw Assumes . NotReachable ( )
207- } ) ;
208- }
209- catch
199+ try
200+ {
201+ await ( update . Value switch
210202 {
211- // Tear down on any exception
212- await DisposeAsync ( ) ;
203+ { EvaluationUpdate : not null } => OnEvaluationUpdateAsync ( update . Derive ( u => u . EvaluationUpdate ! ) ) ,
204+ { BuildUpdate : not null } => OnBuildUpdateAsync ( update . Derive ( u => u . BuildUpdate ! ) ) ,
205+ _ => throw Assumes . NotReachable ( )
206+ } ) ;
207+ }
208+ catch
209+ {
210+ // Tear down on any exception
211+ await DisposeAsync ( ) ;
213212
214- // Exceptions here are product errors, so let the exception escape in order
215- // to produce an upstream NFE.
216- throw ;
217- }
218- } ) ;
213+ // Exceptions here are product errors, so let the exception escape in order
214+ // to produce an upstream NFE.
215+ throw ;
216+ }
217+ }
219218 }
220219
221220 private async Task OnEvaluationUpdateAsync ( IProjectVersionedValue < EvaluationUpdate > evaluationUpdate )
0 commit comments