Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ internal sealed class SolutionEventMonitor : IDisposable

public SolutionEventMonitor(IGlobalOperationNotificationService notificationService)
{
Contract.ThrowIfNull(notificationService);
_notificationService = notificationService;

RegisterEventHandler(KnownUIContexts.SolutionBuildingContext, SolutionBuildingContextChanged);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,77 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Notification;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Shell;

namespace Microsoft.VisualStudio.LanguageServices.ExternalAccess.UnitTesting;

[Export(typeof(IGlobalOperationNotificationService)), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed partial class VisualStudioGlobalOperationNotificationService(
internal sealed partial class VisualStudioGlobalOperationNotificationService : AbstractGlobalOperationNotificationService, IDisposable
{
private readonly SolutionEventMonitor _solutionEventMonitor;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public VisualStudioGlobalOperationNotificationService(
IThreadingContext threadingContext,
IAsynchronousOperationListenerProvider listenerProvider)
: AbstractGlobalOperationNotificationService(listenerProvider, threadingContext.DisposalToken);
IAsynchronousOperationListenerProvider listenerProvider) : base(listenerProvider, threadingContext.DisposalToken)
{
_solutionEventMonitor = new SolutionEventMonitor(this);

// we will pause whatever ambient work loads we have that are tied to IGlobalOperationNotificationService
// such as solution crawler, preemptive remote host synchronization and etc. any background work users
// didn't explicitly asked for.
//
// this should give all resources to BulkFileOperation. we do same for things like build, debugging, wait
// dialog and etc. BulkFileOperation is used for things like git branch switching and etc.

// BulkFileOperation can't have nested events. there will be ever only 1 events (Begin/End)
// so we only need simple tracking.
var gate = new object();
IDisposable? localRegistration = null;

BulkFileOperation.Begin += (s, a) => StartBulkFileOperationNotification();
BulkFileOperation.End += (s, a) => StopBulkFileOperationNotification();

return;

void StartBulkFileOperationNotification()
{
lock (gate)
{
// this shouldn't happen, but we are using external component
// so guarding us from them
if (localRegistration != null)
{
FatalError.ReportAndCatch(new InvalidOperationException("BulkFileOperation already exist"), ErrorSeverity.General);
return;
}

localRegistration = Start("BulkFileOperation");
}
}

void StopBulkFileOperationNotification()
{
lock (gate)
{
// localRegistration may be null if BulkFileOperation was already in the middle of running. So we
// explicitly do not assert that is is non-null here.
localRegistration?.Dispose();
localRegistration = null;
}
}
}

public void Dispose()
{
_solutionEventMonitor.Dispose();
}
}
58 changes: 0 additions & 58 deletions src/VisualStudio/Core/Def/RoslynPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ColorSchemes;
using Microsoft.CodeAnalysis.Common;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Notification;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Remote.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.EditorConfigSettings;
Expand All @@ -33,9 +31,7 @@
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.ServiceBroker;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Threading;
using Roslyn.Utilities;
using Task = System.Threading.Tasks.Task;

namespace Microsoft.VisualStudio.LanguageServices.Setup;
Expand Down Expand Up @@ -109,12 +105,6 @@ Task OnAfterPackageLoadedBackgroundThreadAsync(PackageLoadTasks afterPackageLoad
var colorSchemeApplier = ComponentModel.GetService<ColorSchemeApplier>();
colorSchemeApplier.RegisterInitializationWork(afterPackageLoadedTasks);

// We are at the VS layer, so we know we must be able to get the IGlobalOperationNotificationService here.
var globalNotificationService = this.ComponentModel.GetService<IGlobalOperationNotificationService>();

_solutionEventMonitor = new SolutionEventMonitor(globalNotificationService);
TrackBulkFileOperations(globalNotificationService);

return Task.CompletedTask;
}

Expand Down Expand Up @@ -243,52 +233,4 @@ private void UnregisterRuleSetEventHandler()
_ruleSetEventHandler = null;
}
}

private static void TrackBulkFileOperations(IGlobalOperationNotificationService globalNotificationService)
{
// we will pause whatever ambient work loads we have that are tied to IGlobalOperationNotificationService
// such as solution crawler, preemptive remote host synchronization and etc. any background work users
// didn't explicitly asked for.
//
// this should give all resources to BulkFileOperation. we do same for things like build, debugging, wait
// dialog and etc. BulkFileOperation is used for things like git branch switching and etc.
Contract.ThrowIfNull(globalNotificationService);

// BulkFileOperation can't have nested events. there will be ever only 1 events (Begin/End)
// so we only need simple tracking.
var gate = new object();
IDisposable? localRegistration = null;

BulkFileOperation.Begin += (s, a) => StartBulkFileOperationNotification();
BulkFileOperation.End += (s, a) => StopBulkFileOperationNotification();

return;

void StartBulkFileOperationNotification()
{
lock (gate)
{
// this shouldn't happen, but we are using external component
// so guarding us from them
if (localRegistration != null)
{
FatalError.ReportAndCatch(new InvalidOperationException("BulkFileOperation already exist"), ErrorSeverity.General);
return;
}

localRegistration = globalNotificationService.Start("BulkFileOperation");
}
}

void StopBulkFileOperationNotification()
{
lock (gate)
{
// localRegistration may be null if BulkFileOperation was already in the middle of running. So we
// explicitly do not assert that is is non-null here.
localRegistration?.Dispose();
localRegistration = null;
}
}
}
}
Loading