PageRenderTime 170ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/src/VisualStudio/Core/Def/Implementation/VirtualMemoryNotificationListener.cs

https://gitlab.com/sharadag/Roslyn
C# | 161 lines | 103 code | 23 blank | 35 comment | 7 complexity | 3019154e845c8d2d4d1d3ceee083def3 MD5 | raw file
  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. using System;
  3. using System.Composition;
  4. using System.Linq;
  5. using Microsoft.CodeAnalysis;
  6. using Microsoft.CodeAnalysis.Editor.Shared.Options;
  7. using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
  8. using Microsoft.CodeAnalysis.Extensions;
  9. using Microsoft.CodeAnalysis.Host;
  10. using Microsoft.CodeAnalysis.Internal.Log;
  11. using Microsoft.CodeAnalysis.Options;
  12. using Microsoft.CodeAnalysis.Shared.Options;
  13. using Microsoft.VisualStudio.LanguageServices.Implementation;
  14. using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
  15. using Microsoft.VisualStudio.Shell;
  16. using Microsoft.VisualStudio.Shell.Interop;
  17. namespace Microsoft.VisualStudio.LanguageServices
  18. {
  19. /// <summary>
  20. /// Listens to broadcast notifications from the Visual Studio Shell indicating that the application is running
  21. /// low on available virtual memory.
  22. /// </summary>
  23. [Export, Shared]
  24. internal sealed class VirtualMemoryNotificationListener : ForegroundThreadAffinitizedObject, IVsBroadcastMessageEvents
  25. {
  26. // memory threshold to turn off full solution analysis - 200MB
  27. private const long MemoryThreshold = 200 * 1024 * 1024;
  28. // low vm more info page link
  29. private const string LowVMMoreInfoLink = "http://go.microsoft.com/fwlink/?LinkID=799402&clcid=0x409";
  30. private readonly VisualStudioWorkspace _workspace;
  31. private readonly WorkspaceCacheService _workspaceCacheService;
  32. private bool _alreadyLogged;
  33. [ImportingConstructor]
  34. private VirtualMemoryNotificationListener(
  35. SVsServiceProvider serviceProvider,
  36. VisualStudioWorkspace workspace) : base(assertIsForeground: true)
  37. {
  38. _workspace = workspace;
  39. _workspace.WorkspaceChanged += OnWorkspaceChanged;
  40. _workspaceCacheService = workspace.Services.GetService<IWorkspaceCacheService>() as WorkspaceCacheService;
  41. var shell = (IVsShell)serviceProvider.GetService(typeof(SVsShell));
  42. // Note: We never unhook this event sink. It lives for the lifetime of the host.
  43. uint cookie;
  44. ErrorHandler.ThrowOnFailure(shell.AdviseBroadcastMessages(this, out cookie));
  45. }
  46. /// <summary>
  47. /// Called by the Visual Studio Shell to notify components of a broadcast message.
  48. /// </summary>
  49. /// <param name="msg">The message identifier.</param>
  50. /// <param name="wParam">First parameter associated with the message.</param>
  51. /// <param name="lParam">Second parameter associated with the message.</param>
  52. /// <returns>S_OK always.</returns>
  53. public int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam)
  54. {
  55. switch (msg)
  56. {
  57. case VSConstants.VSM_VIRTUALMEMORYLOW:
  58. case VSConstants.VSM_VIRTUALMEMORYCRITICAL:
  59. {
  60. if (!_alreadyLogged)
  61. {
  62. // record that we had hit critical memory barrier
  63. Logger.Log(FunctionId.VirtualMemory_MemoryLow, KeyValueLogMessage.Create(m =>
  64. {
  65. // which message we are logging and memory left in bytes when this is called.
  66. m["MSG"] = msg;
  67. m["MemoryLeft"] = (long)wParam;
  68. }));
  69. _alreadyLogged = true;
  70. }
  71. _workspaceCacheService?.FlushCaches();
  72. // turn off full solution analysis only if user option is on.
  73. if (ShouldTurnOffFullSolutionAnalysis((long)wParam))
  74. {
  75. // turn our full solution analysis option off.
  76. // if user full solution analysis option is on, then we will show info bar to users to restore it.
  77. // if user full solution analysis option is off, then setting this doesn't matter. full solution analysis is already off.
  78. _workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, false);
  79. if (IsUserOptionOn())
  80. {
  81. // let user know full analysis is turned off due to memory concern.
  82. // make sure we show info bar only once for the same solution.
  83. _workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysisInfoBarShown, true);
  84. _workspace.Services.GetService<IErrorReportingService>().ShowErrorInfo(ServicesVSResources.FullSolutionAnalysisOff,
  85. new ErrorReportingUI(ServicesVSResources.Reenable, ErrorReportingUI.UIKind.Button, () =>
  86. _workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, true)),
  87. new ErrorReportingUI(ServicesVSResources.LearnMore, ErrorReportingUI.UIKind.HyperLink, () =>
  88. BrowserHelper.StartBrowser(new Uri(LowVMMoreInfoLink)), closeAfterAction: false));
  89. }
  90. }
  91. // turn off low latency GC mode.
  92. // once we hit this, not hitting "Out of memory" exception is more important than typing being smooth all the time.
  93. // once it is turned off, user will hit time to time keystroke which responsive time is more than 50ms. in our own perf lab,
  94. // about 1-2% was over 50ms with this off when we first introduced this GC mode.
  95. GCManager.TurnOffLowLatencyMode();
  96. break;
  97. }
  98. }
  99. return VSConstants.S_OK;
  100. }
  101. private bool ShouldTurnOffFullSolutionAnalysis(long availableMemory)
  102. {
  103. // conditions
  104. // 1. if available memory is less than the threshold and
  105. // 2. if full solution analysis memory monitor is on (user can set it off using registery, when he does, we will never show info bar) and
  106. // 3. if our full solution analysis option is on (not user full solution analysis option, but our internal one) and
  107. // 4. if infobar is never shown to users for this solution
  108. return availableMemory < MemoryThreshold &&
  109. _workspace.Options.GetOption(InternalFeatureOnOffOptions.FullSolutionAnalysisMemoryMonitor) &&
  110. _workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysis) &&
  111. !_workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysisInfoBarShown);
  112. }
  113. private bool IsUserOptionOn()
  114. {
  115. // check languages currently on solution. since we only show info bar once, we don't need to track solution changes.
  116. var languages = _workspace.CurrentSolution.Projects.Select(p => p.Language).Distinct();
  117. foreach (var language in languages)
  118. {
  119. if (ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(_workspace, language))
  120. {
  121. return true;
  122. }
  123. }
  124. return false;
  125. }
  126. private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
  127. {
  128. if (e.Kind != WorkspaceChangeKind.SolutionAdded)
  129. {
  130. return;
  131. }
  132. // first make sure full solution analysis is on. (not user options but our internal options. even if our option is on, if user option is off
  133. // full solution analysis won't run. also, reset infobar state.
  134. _workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysisInfoBarShown, false)
  135. .WithChangedOption(RuntimeOptions.FullSolutionAnalysis, true);
  136. }
  137. }
  138. }