Skip to content

Commit cfe00ec

Browse files
Move the initialization of the global notification service
We were initializing this in package load, because at one point the APIs to do so had to be on the UI thread. At this point the only remaining use of this service is for the legacy solution crawler, so there's no point in initializing it until the service is actually constructed. And since everything is now free threaded, it's easy to move.
1 parent 8e3d845 commit cfe00ec

File tree

3 files changed

+65
-57
lines changed

3 files changed

+65
-57
lines changed

src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/SolutionEventMonitor.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ internal sealed class SolutionEventMonitor : IDisposable
2626

2727
public SolutionEventMonitor(IGlobalOperationNotificationService notificationService)
2828
{
29-
Contract.ThrowIfNull(notificationService);
3029
_notificationService = notificationService;
3130

3231
RegisterEventHandler(KnownUIContexts.SolutionBuildingContext, SolutionBuildingContextChanged);

src/VisualStudio/Core/Def/ExternalAccess/UnitTesting/VisualStudioGlobalOperationNotificationService.cs

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,77 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Collections.Generic;
67
using System.Composition;
78
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
9+
using Microsoft.CodeAnalysis.ErrorReporting;
810
using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Notification;
911
using Microsoft.CodeAnalysis.Host.Mef;
1012
using Microsoft.CodeAnalysis.Shared.TestHooks;
13+
using Microsoft.VisualStudio.Shell;
1114

1215
namespace Microsoft.VisualStudio.LanguageServices.ExternalAccess.UnitTesting;
1316

1417
[Export(typeof(IGlobalOperationNotificationService)), Shared]
15-
[method: ImportingConstructor]
16-
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
17-
internal sealed partial class VisualStudioGlobalOperationNotificationService(
18+
internal sealed partial class VisualStudioGlobalOperationNotificationService : AbstractGlobalOperationNotificationService, IDisposable
19+
{
20+
private readonly SolutionEventMonitor _solutionEventMonitor;
21+
22+
[ImportingConstructor]
23+
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
24+
public VisualStudioGlobalOperationNotificationService(
1825
IThreadingContext threadingContext,
19-
IAsynchronousOperationListenerProvider listenerProvider)
20-
: AbstractGlobalOperationNotificationService(listenerProvider, threadingContext.DisposalToken);
26+
IAsynchronousOperationListenerProvider listenerProvider) : base(listenerProvider, threadingContext.DisposalToken)
27+
{
28+
_solutionEventMonitor = new SolutionEventMonitor(this);
29+
30+
// we will pause whatever ambient work loads we have that are tied to IGlobalOperationNotificationService
31+
// such as solution crawler, preemptive remote host synchronization and etc. any background work users
32+
// didn't explicitly asked for.
33+
//
34+
// this should give all resources to BulkFileOperation. we do same for things like build, debugging, wait
35+
// dialog and etc. BulkFileOperation is used for things like git branch switching and etc.
36+
37+
// BulkFileOperation can't have nested events. there will be ever only 1 events (Begin/End)
38+
// so we only need simple tracking.
39+
var gate = new object();
40+
IDisposable? localRegistration = null;
41+
42+
BulkFileOperation.Begin += (s, a) => StartBulkFileOperationNotification();
43+
BulkFileOperation.End += (s, a) => StopBulkFileOperationNotification();
44+
45+
return;
46+
47+
void StartBulkFileOperationNotification()
48+
{
49+
lock (gate)
50+
{
51+
// this shouldn't happen, but we are using external component
52+
// so guarding us from them
53+
if (localRegistration != null)
54+
{
55+
FatalError.ReportAndCatch(new InvalidOperationException("BulkFileOperation already exist"), ErrorSeverity.General);
56+
return;
57+
}
58+
59+
localRegistration = Start("BulkFileOperation");
60+
}
61+
}
62+
63+
void StopBulkFileOperationNotification()
64+
{
65+
lock (gate)
66+
{
67+
// localRegistration may be null if BulkFileOperation was already in the middle of running. So we
68+
// explicitly do not assert that is is non-null here.
69+
localRegistration?.Dispose();
70+
localRegistration = null;
71+
}
72+
}
73+
}
74+
75+
public void Dispose()
76+
{
77+
_solutionEventMonitor.Dispose();
78+
}
79+
}

src/VisualStudio/Core/Def/RoslynPackage.cs

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion;
1515
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
1616
using Microsoft.CodeAnalysis.ErrorReporting;
17-
using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Notification;
1817
using Microsoft.CodeAnalysis.Options;
1918
using Microsoft.CodeAnalysis.Remote.ProjectSystem;
2019
using Microsoft.VisualStudio.LanguageServices.EditorConfigSettings;
@@ -109,12 +108,6 @@ Task OnAfterPackageLoadedBackgroundThreadAsync(PackageLoadTasks afterPackageLoad
109108
var colorSchemeApplier = ComponentModel.GetService<ColorSchemeApplier>();
110109
colorSchemeApplier.RegisterInitializationWork(afterPackageLoadedTasks);
111110

112-
// We are at the VS layer, so we know we must be able to get the IGlobalOperationNotificationService here.
113-
var globalNotificationService = this.ComponentModel.GetService<IGlobalOperationNotificationService>();
114-
115-
_solutionEventMonitor = new SolutionEventMonitor(globalNotificationService);
116-
TrackBulkFileOperations(globalNotificationService);
117-
118111
return Task.CompletedTask;
119112
}
120113

@@ -246,49 +239,6 @@ private void UnregisterRuleSetEventHandler()
246239

247240
private static void TrackBulkFileOperations(IGlobalOperationNotificationService globalNotificationService)
248241
{
249-
// we will pause whatever ambient work loads we have that are tied to IGlobalOperationNotificationService
250-
// such as solution crawler, preemptive remote host synchronization and etc. any background work users
251-
// didn't explicitly asked for.
252-
//
253-
// this should give all resources to BulkFileOperation. we do same for things like build, debugging, wait
254-
// dialog and etc. BulkFileOperation is used for things like git branch switching and etc.
255-
Contract.ThrowIfNull(globalNotificationService);
256-
257-
// BulkFileOperation can't have nested events. there will be ever only 1 events (Begin/End)
258-
// so we only need simple tracking.
259-
var gate = new object();
260-
IDisposable? localRegistration = null;
261-
262-
BulkFileOperation.Begin += (s, a) => StartBulkFileOperationNotification();
263-
BulkFileOperation.End += (s, a) => StopBulkFileOperationNotification();
264-
265-
return;
266-
267-
void StartBulkFileOperationNotification()
268-
{
269-
lock (gate)
270-
{
271-
// this shouldn't happen, but we are using external component
272-
// so guarding us from them
273-
if (localRegistration != null)
274-
{
275-
FatalError.ReportAndCatch(new InvalidOperationException("BulkFileOperation already exist"), ErrorSeverity.General);
276-
return;
277-
}
278-
279-
localRegistration = globalNotificationService.Start("BulkFileOperation");
280-
}
281-
}
282-
283-
void StopBulkFileOperationNotification()
284-
{
285-
lock (gate)
286-
{
287-
// localRegistration may be null if BulkFileOperation was already in the middle of running. So we
288-
// explicitly do not assert that is is non-null here.
289-
localRegistration?.Dispose();
290-
localRegistration = null;
291-
}
292-
}
242+
293243
}
294244
}

0 commit comments

Comments
 (0)