|
3 | 3 | // See the LICENSE file in the project root for more information. |
4 | 4 |
|
5 | 5 | using System; |
| 6 | +using System.Collections.Generic; |
6 | 7 | using System.Composition; |
7 | 8 | using Microsoft.CodeAnalysis.Editor.Shared.Utilities; |
| 9 | +using Microsoft.CodeAnalysis.ErrorReporting; |
8 | 10 | using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Notification; |
9 | 11 | using Microsoft.CodeAnalysis.Host.Mef; |
10 | 12 | using Microsoft.CodeAnalysis.Shared.TestHooks; |
| 13 | +using Microsoft.VisualStudio.Shell; |
11 | 14 |
|
12 | 15 | namespace Microsoft.VisualStudio.LanguageServices.ExternalAccess.UnitTesting; |
13 | 16 |
|
14 | 17 | [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( |
18 | 25 | 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 | +} |
0 commit comments