From 020f5ad3cc570e544f39ac0a16a0da31e557c560 Mon Sep 17 00:00:00 2001 From: Heejae Chang Date: Tue, 5 Jan 2016 04:30:23 -0800 Subject: [PATCH] use diagnostic events task scheduler so that we don't flood async event queue --- .../DiagnosticAnalyzerService_UpdateSource.cs | 5 +- .../DiagnosticEventTaskScheduler.cs | 55 +++++++++++++++++++ .../Portable/Diagnostics/DiagnosticService.cs | 5 +- ...sticIncrementalAnalyzer.DiagnosticState.cs | 10 +++- ...gnosticIncrementalAnalyzer.StateManager.cs | 2 +- .../EngineV1/DiagnosticIncrementalAnalyzer.cs | 13 +++-- src/Features/Core/Portable/Features.csproj | 1 + 7 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 src/Features/Core/Portable/Diagnostics/DiagnosticEventTaskScheduler.cs diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs index b742e336d8c..3f74ec02dd8 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_UpdateSource.cs @@ -20,7 +20,10 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticUpdateSource private DiagnosticAnalyzerService(IDiagnosticUpdateSourceRegistrationService registrationService) : this() { _eventMap = new EventMap(); - _eventQueue = new SimpleTaskQueue(TaskScheduler.Default); + + // use diagnostic event task scheduler so that we never flood async events queue with million of events. + // queue itself can handle huge number of events but we are seeing OOM due to captured data in pending events. + _eventQueue = new SimpleTaskQueue(new DiagnosticEventTaskScheduler(blockingUpperBound: 100)); registrationService.Register(this); } diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticEventTaskScheduler.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticEventTaskScheduler.cs new file mode 100644 index 00000000000..a79d647a01c --- /dev/null +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticEventTaskScheduler.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Diagnostics +{ + /// + /// This task scheduler will block queuing new tasks if upper bound has met. + /// + internal class DiagnosticEventTaskScheduler : TaskScheduler + { + private readonly Task _mainTask; + private readonly BlockingCollection _tasks; + + public DiagnosticEventTaskScheduler(int blockingUpperBound) + { + _tasks = new BlockingCollection(blockingUpperBound); + + // portable layer doesnt support explicit thread creation. use long running task to create and hold onto a thread + _mainTask = Task.Factory.SafeStartNew(Start, CancellationToken.None, + TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning, TaskScheduler.Default); + } + + private void Start() + { + while (true) + { + var task = _tasks.Take(); + bool ret = this.TryExecuteTask(task); + } + } + + protected override void QueueTask(Task task) + { + _tasks.Add(task); + } + + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + // NOTE: TPL will ensure only one task ever run when running scheduled task. and since this is only used + // in diagnostic events, we know task will ever run sequencely. so no worry about reverted order here. + return this.TryExecuteTask(task); + } + + protected override IEnumerable GetScheduledTasks() + { + // debugger will use this method to get scheduled tasks for this scheduler + return _tasks.ToArray(); + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticService.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticService.cs index 3dbbe6624e4..ab368dc658a 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticService.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticService.cs @@ -30,7 +30,10 @@ public DiagnosticService([ImportMany] IEnumerable + /// Only For Testing + /// + internal string Name_TestingOnly { get { return _stateName; } } - internal string Language + /// + /// Only For Testing + /// + internal string Language_TestingOnly { get { return _language; } } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.StateManager.cs index 85de338c151..2865384195c 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -178,7 +178,7 @@ private static void VerifyDiagnosticStates(IEnumerable stateSets) { var state = stateSet.GetState((StateType)i); - if (!(set.Add(ValueTuple.Create(state.Language, state.Name)))) + if (!(set.Add(ValueTuple.Create(state.Language_TestingOnly, state.Name_TestingOnly)))) { Contract.Fail(); } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs index 7613d033568..438ec74813c 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV1/DiagnosticIncrementalAnalyzer.cs @@ -181,7 +181,7 @@ private async Task AnalyzeSyntaxAsync(Document document, ImmutableHashSet.Empty, data.Items); continue; } @@ -617,6 +617,11 @@ private static bool CheckSemanticVersions(Project project, AnalysisData existing project.CanReusePersistedDependentSemanticVersion(versions.ProjectVersion, versions.DataVersion, existingData.DataVersion); } + private void RaiseDiagnosticCreatedFromCacheIfNeeded(StateType type, Document document, StateSet stateSet, ImmutableArray items) + { + RaiseDocumentDiagnosticsUpdatedIfNeeded(type, document, stateSet, ImmutableArray.Empty, items); + } + private void RaiseDocumentDiagnosticsUpdatedIfNeeded( StateType type, Document document, StateSet stateSet, ImmutableArray existingItems, ImmutableArray newItems) { diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index a8d1522d17c..3cfcc45b0a9 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -177,6 +177,7 @@ + -- GitLab