-
Notifications
You must be signed in to change notification settings - Fork 355
Improve Memory Usage by not holding onto multiple copies of GlobalPackages
#861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
f7d0a49
Cache global packages for reuse between projects
mjbvz 105b472
Move cache to nodemodules node
mjbvz 47adde3
Small fixes
mjbvz f92f6fe
Merge branch 'master' into controller-global-package-provider
mjbvz fa8e907
Restore old lock
mjbvz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,9 +16,9 @@ | |
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.IO; | ||
| using System.Linq; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.NodejsTools.Npm; | ||
| using Microsoft.NodejsTools.NpmUI; | ||
| using Microsoft.VisualStudio; | ||
|
|
@@ -30,6 +30,33 @@ | |
|
|
||
| namespace Microsoft.NodejsTools.Project { | ||
| internal class NodeModulesNode : AbstractNpmNode { | ||
| #region StaticFields | ||
| private static INpmGlobalPackageProvider _cachingGlobalPackageProvider = new CacheGlobalNpmPackageProvider(); | ||
|
|
||
| /// <summary> | ||
| /// `INpmGlobalPackageProvider` that caches `IGlobalPackages` objects for reuse. | ||
| /// </summary> | ||
| private class CacheGlobalNpmPackageProvider : INpmGlobalPackageProvider { | ||
| private Dictionary<string, WeakReference<IGlobalPackages>> _packages = new Dictionary<string, WeakReference<IGlobalPackages>>(); | ||
|
|
||
| public async Task<IGlobalPackages> GetGlobalPackages(string pathToNpm) { | ||
| WeakReference<IGlobalPackages> reference; | ||
| if (_packages.TryGetValue(pathToNpm, out reference)) { | ||
| IGlobalPackages current; | ||
| if (reference.TryGetTarget(out current) && current != null) { | ||
| return current; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: the less generic names, the better |
||
| } | ||
| } | ||
| var delegateProvider = new Npm.SPI.NpmBinGlobalPackageProvider(); | ||
| var globals = await delegateProvider.GetGlobalPackages(pathToNpm); | ||
| if (globals != null) { | ||
| _packages[pathToNpm] = new WeakReference<IGlobalPackages>(globals); | ||
| } | ||
| return globals; | ||
| } | ||
| } | ||
| #endregion | ||
|
|
||
| #region Constants | ||
|
|
||
| /// <summary> | ||
|
|
@@ -66,7 +93,7 @@ internal class NodeModulesNode : AbstractNpmNode { | |
|
|
||
| public NodeModulesNode(NodejsProjectNode root) | ||
| : base(root) { | ||
| _npmController = DefaultNpmController(_projectNode.ProjectHome, new NpmPathProvider(this)); | ||
| _npmController = DefaultNpmController(); | ||
| RegisterWithNpmController(_npmController); | ||
|
|
||
| _globalModulesNode = new GlobalModulesNode(root, this); | ||
|
|
@@ -131,12 +158,13 @@ public string PathToNpm { | |
| } | ||
| } | ||
|
|
||
| private static INpmController DefaultNpmController(string projectHome, NpmPathProvider pathProvider) { | ||
| private INpmController DefaultNpmController() { | ||
| return NpmControllerFactory.Create( | ||
| projectHome, | ||
| _projectNode.ProjectHome, | ||
| NodejsPackage.Instance.NpmOptionsPage.NpmCachePath, | ||
| false, | ||
| pathProvider); | ||
| new NpmPathProvider(this), | ||
| _cachingGlobalPackageProvider); | ||
| } | ||
|
|
||
| private void RegisterWithNpmController(INpmController controller) { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,20 +15,19 @@ | |
| //*********************************************************// | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace Microsoft.NodejsTools.Npm.SPI { | ||
| internal class NpmController : AbstractNpmLogSource, INpmController { | ||
|
|
||
| private IPackageCatalog _sRepoCatalog; | ||
| private string _fullPathToRootPackageDirectory; | ||
| private string _cachePath; | ||
| private bool _showMissingDevOptionalSubPackages; | ||
| private INpmPathProvider _npmPathProvider; | ||
| private INpmGlobalPackageProvider _globalPackageProvider; | ||
| private IRootPackage _rootPackage; | ||
| private IGlobalPackages _globalPackage; | ||
| private readonly object _lock = new object(); | ||
|
|
@@ -41,19 +40,20 @@ internal class NpmController : AbstractNpmLogSource, INpmController { | |
|
|
||
| private readonly object _fileBitsLock = new object(); | ||
|
|
||
|
|
||
| private bool _isDisposed; | ||
| private bool _isReloadingModules = false; | ||
|
|
||
| public NpmController( | ||
| string fullPathToRootPackageDirectory, | ||
| string cachePath, | ||
| bool showMissingDevOptionalSubPackages = false, | ||
| INpmPathProvider npmPathProvider = null) { | ||
| INpmPathProvider npmPathProvider = null, | ||
| INpmGlobalPackageProvider globalPackageProvider = null) { | ||
| _fullPathToRootPackageDirectory = fullPathToRootPackageDirectory; | ||
| _cachePath = cachePath; | ||
| _showMissingDevOptionalSubPackages = showMissingDevOptionalSubPackages; | ||
| _npmPathProvider = npmPathProvider; | ||
| _globalPackageProvider = globalPackageProvider ?? new NpmBinGlobalPackageProvider(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not create a CacheGlobalPackageProvider here instead? |
||
|
|
||
| _localWatcher = CreateModuleDirectoryWatcherIfDirectoryExists(_fullPathToRootPackageDirectory); | ||
| _globalWatcher = CreateModuleDirectoryWatcherIfDirectoryExists(this.ListBaseDirectory); | ||
|
|
@@ -127,11 +127,8 @@ public async Task RefreshAsync() { | |
| _fullPathToRootPackageDirectory, | ||
| _showMissingDevOptionalSubPackages); | ||
|
|
||
| var command = new NpmBinCommand(_fullPathToRootPackageDirectory, true, PathToNpm); | ||
| GlobalPackages = await _globalPackageProvider.GetGlobalPackages(PathToNpm); | ||
|
|
||
| GlobalPackages = (await command.ExecuteAsync()) | ||
| ? RootPackageFactory.Create(command.BinDirectory) | ||
| : null; | ||
| } catch (IOException) { | ||
| // Can sometimes happen when packages are still installing because the file may still be used by another process | ||
| } finally { | ||
|
|
@@ -364,4 +361,16 @@ public void Dispose() { | |
| } | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Standard `INpmGlobalPackageProvider` that uses `npm bin` to get global packages. | ||
| /// </summary> | ||
| public class NpmBinGlobalPackageProvider : INpmGlobalPackageProvider { | ||
| public async Task<IGlobalPackages> GetGlobalPackages(string pathToNpm) { | ||
| var command = new NpmBinCommand(String.Empty, true, pathToNpm); | ||
| return (await command.ExecuteAsync()) | ||
| ? RootPackageFactory.Create(command.BinDirectory) | ||
| : null; | ||
| } | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really sure i see a significant enough advantage of using weak references here, especially since a strong reference is already being held onto by the project node and I don't anticipate a) the global folder frequently changing between projects and b) projects being loaded and unloaded often. Am I missing something?