DiagnosticAnalyzerService_UpdateSource.cs 3.8 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
2 3 4 5

using System;
using System.Collections.Immutable;
using System.Threading;
H
Heejae Chang 已提交
6
using Microsoft.CodeAnalysis.Shared.TestHooks;
7 8 9 10 11 12
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Diagnostics
{
    internal partial class DiagnosticAnalyzerService : IDiagnosticUpdateSource
    {
H
Heejae Chang 已提交
13 14
        private const string DiagnosticsUpdatedEventName = "DiagnosticsUpdated";

H
Heejae Chang 已提交
15 16
        private static readonly DiagnosticEventTaskScheduler s_eventScheduler = new DiagnosticEventTaskScheduler(blockingUpperBound: 100);

H
Heejae Chang 已提交
17 18 19
        // use eventMap and taskQueue to serialize events
        private readonly EventMap _eventMap;
        private readonly SimpleTaskQueue _eventQueue;
20

H
Heejae Chang 已提交
21
        private DiagnosticAnalyzerService(IDiagnosticUpdateSourceRegistrationService registrationService) : this()
22
        {
H
Heejae Chang 已提交
23
            _eventMap = new EventMap();
24 25 26

            // 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.
H
Heejae Chang 已提交
27
            _eventQueue = new SimpleTaskQueue(s_eventScheduler);
H
Heejae Chang 已提交
28

29 30 31
            registrationService.Register(this);
        }

H
Heejae Chang 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44
        public event EventHandler<DiagnosticsUpdatedArgs> DiagnosticsUpdated
        {
            add
            {
                _eventMap.AddEventHandler(DiagnosticsUpdatedEventName, value);
            }

            remove
            {
                _eventMap.RemoveEventHandler(DiagnosticsUpdatedEventName, value);
            }
        }

45
        internal void RaiseDiagnosticsUpdated(DiagnosticsUpdatedArgs args)
46
        {
47
            // all diagnostics events are serialized.
H
Heejae Chang 已提交
48 49 50 51
            var ev = _eventMap.GetEventHandlers<EventHandler<DiagnosticsUpdatedArgs>>(DiagnosticsUpdatedEventName);
            if (ev.HasHandlers)
            {
                var asyncToken = Listener.BeginAsyncOperation(nameof(RaiseDiagnosticsUpdated));
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
                _eventQueue.ScheduleTask(() => ev.RaiseEvent(handler => handler(this, args))).CompletesAsyncOperation(asyncToken);
            }
        }

        internal void RaiseBulkDiagnosticsUpdated(Action<Action<DiagnosticsUpdatedArgs>> eventAction)
        {
            // all diagnostics events are serialized.
            var ev = _eventMap.GetEventHandlers<EventHandler<DiagnosticsUpdatedArgs>>(DiagnosticsUpdatedEventName);
            if (ev.HasHandlers)
            {
                // we do this bulk update to reduce number of tasks (with captured data) enqueued.
                // we saw some "out of memory" due to us having long list of pending tasks in memory. 
                // this is to reduce for such case to happen.
                Action<DiagnosticsUpdatedArgs> raiseEvents = args => ev.RaiseEvent(handler => handler(this, args));

                var asyncToken = Listener.BeginAsyncOperation(nameof(RaiseDiagnosticsUpdated));
                _eventQueue.ScheduleTask(() => eventAction(raiseEvents)).CompletesAsyncOperation(asyncToken);
H
Heejae Chang 已提交
69
            }
70 71 72 73
        }

        bool IDiagnosticUpdateSource.SupportGetDiagnostics { get { return true; } }

74
        ImmutableArray<DiagnosticData> IDiagnosticUpdateSource.GetDiagnostics(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics, CancellationToken cancellationToken)
75 76 77
        {
            if (id != null)
            {
78
                return GetSpecificCachedDiagnosticsAsync(workspace, id, includeSuppressedDiagnostics, cancellationToken).WaitAndGetResult(cancellationToken);
79 80
            }

81
            return GetCachedDiagnosticsAsync(workspace, projectId, documentId, includeSuppressedDiagnostics, cancellationToken).WaitAndGetResult(cancellationToken);
82 83 84
        }
    }
}