File: Implementation\GCManager.cs
Web Access
Project: src\src\VisualStudio\Core\Def\Microsoft.VisualStudio.LanguageServices_pxr0p0dn_wpftmp.csproj (Microsoft.VisualStudio.LanguageServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Runtime;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation;
 
/// <summary>
/// This class manages setting the GC mode to SustainedLowLatency.
/// 
/// It is safe to call from any thread, but is intended to be called from
/// the UI thread whenever user keyboard or mouse input is received.
/// </summary>
internal static class GCManager
{
    // The default delay can be overridden by setting <VS Registry>\Performance : SustainedLowLatencyDuration
    private static int s_delayMilliseconds = 0;
    private const int DefaultDelayMilliseconds = 5000;
 
    private static ResettableDelay s_delay;
 
    static GCManager()
    {
        // Allow disabling SustainedLowLatency by setting the reg key value to 0
        System.Threading.Tasks.Task.Run(() =>
        {
            using (var root = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_UserSettings))
            {
                if (root != null)
                {
                    using var key = root.OpenSubKey("Performance");
                    const string name = "SustainedLowLatencyDuration";
                    if (key != null && key.GetValue(name) != null && key.GetValueKind(name) == Microsoft.Win32.RegistryValueKind.DWord)
                    {
                        s_delayMilliseconds = (int)key.GetValue(name, s_delayMilliseconds);
                        return;
                    }
                }
            }
 
            s_delayMilliseconds = DefaultDelayMilliseconds;
        });
    }
 
    /// <summary>
    /// Turn off low latency GC mode.
    /// 
    /// if there is a pending low latency mode request, Latency mode will go back to its original status as
    /// pending request timeout. once it goes back to its original status, it will not go back to low latency mode again.
    /// </summary>
    internal static void TurnOffLowLatencyMode()
    {
        // set delay to 0 to turn off the use of sustained low latency
        s_delayMilliseconds = 0;
    }
 
    /// <summary>
    /// Call this method to suppress expensive blocking Gen 2 garbage GCs in
    /// scenarios where high-latency is unacceptable (e.g. processing typing input).
    /// 
    /// Blocking GCs will be re-enabled automatically after a short duration unless 
    /// UseLowLatencyModeForProcessingUserInput is called again.
    /// </summary>
    internal static void UseLowLatencyModeForProcessingUserInput()
    {
        if (s_delayMilliseconds <= 0)
        {
            // The registry key to opt out of Roslyn's SustainedLowLatency is set, or
            // we haven't yet initialized the value.
            return;
        }
 
        var currentMode = GCSettings.LatencyMode;
        var currentDelay = s_delay;
        if (currentMode != GCLatencyMode.SustainedLowLatency)
        {
            GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
 
            // Restore the LatencyMode a short duration after the last UseLowLatencyModeForProcessingUserInput.
            currentDelay = new ResettableDelay(s_delayMilliseconds, AsynchronousOperationListenerProvider.NullListener);
            RestoreGCLatencyModeWhenCompleteAsync(currentDelay.Task, currentMode).ReportNonFatalErrorAsync();
            s_delay = currentDelay;
        }
 
        currentDelay?.Reset();
        return;
 
        async Task RestoreGCLatencyModeWhenCompleteAsync(Task task, GCLatencyMode latencyMode)
        {
            try
            {
                await task.ConfigureAwait(false);
            }
            finally
            {
                RestoreGCLatencyMode(latencyMode);
            }
        }
    }
 
    private static void RestoreGCLatencyMode(GCLatencyMode originalMode)
    {
        GCSettings.LatencyMode = originalMode;
        s_delay = null;
    }
}