-
Notifications
You must be signed in to change notification settings - Fork 414
Avoid NuGet restore cycles #8239
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
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
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
9 changes: 9 additions & 0 deletions
9
src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/FeatureFlags.cs
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 |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // 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.md file in the project root for more information. | ||
|
|
||
| namespace Microsoft.VisualStudio.ProjectSystem.VS | ||
| { | ||
| internal class FeatureFlags | ||
| { | ||
| internal const string EnableNuGetRestoreCycleDetection = "ManagedProjectSystem.EnableNuGetRestoreCycleDetection"; | ||
| } | ||
| } |
106 changes: 106 additions & 0 deletions
106
...dio.ProjectSystem.Managed.VS/ProjectSystem/VS/PackageRestore/NuGetRestoreCycleDetector.cs
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 |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| // 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.md file in the project root for more information. | ||
|
|
||
| namespace Microsoft.VisualStudio.ProjectSystem.VS.PackageRestore | ||
| { | ||
| // This algorithm is to detect the pattern A -> B -> A -> B -> A in the most recent N values. | ||
| // To keep track of the most N recent values we are using a queue of fixed size, where: | ||
| // The most recent value is inserted at the front in O(1) time, and the oldest value is removed at the | ||
| // back in O(1) time. | ||
| // The counter will keep track of how many times values have appeared in the queue consecutively. | ||
| // i.e. | ||
| // A -> B -> C -> A -> B -> D -> X -> Y -> X -> Y -> X -> Y -> X | ||
| // |---cycle detected----| | ||
| internal sealed class NuGetRestoreCycleDetector | ||
| { | ||
| // The fixed size of the queue | ||
| private readonly int _size = 5; | ||
|
|
||
| private readonly object _lock; | ||
| private readonly Queue<byte[]> _values; | ||
| private readonly Dictionary<byte[], int> _lookupTable; | ||
| private int _counter; | ||
|
|
||
| /// <summary> | ||
| /// Fixed size of the numbers of values to store. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// This represents how deep to search for hash cycle. | ||
| /// </remarks> | ||
| public int Size { get; private set; } | ||
|
|
||
| public NuGetRestoreCycleDetector() | ||
| { | ||
| _lock = new object(); | ||
| _values = new Queue<byte[]>(); | ||
| _lookupTable = new Dictionary<byte[], int>(); | ||
| Size = _size; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Validate if hash1 cycle exist. | ||
| /// If no cycle exist, then the hashValue is stored. | ||
| /// </summary> | ||
| /// <param name="hashValue">This is hashValue that is used to verify if it exists in previous restores</param> | ||
| /// <returns>True if it contains hash1 cycle, otherwise false</returns> | ||
| public bool ComputeCycleDetection(byte[] hashValue) | ||
| { | ||
| lock (_lock) | ||
| { | ||
| if (QueueContainsValue(hashValue)) | ||
| { | ||
| _counter++; | ||
|
|
||
| // Verify that a hashValue has repeated in almost all cases | ||
| if (_counter > Size) | ||
| { | ||
| Clear(); | ||
| return true; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| _counter = 0; | ||
| } | ||
|
|
||
| Add(hashValue); | ||
| } | ||
|
|
||
| return false; | ||
|
|
||
| bool QueueContainsValue(byte[] hashValue) | ||
| { | ||
| if (_lookupTable.TryGetValue(hashValue, out int hashCounter) && hashCounter > 0) | ||
| { | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| private void Add(byte[] newHashValue) | ||
| { | ||
| if (_values.Count >= Size) | ||
| { | ||
| var oldestHashValue = _values.Dequeue(); | ||
| _lookupTable[oldestHashValue]--; | ||
| } | ||
|
|
||
| _values.Enqueue(newHashValue); | ||
| if (_lookupTable.ContainsKey(newHashValue)) | ||
| { | ||
| _lookupTable[newHashValue]++; | ||
| } | ||
| else | ||
| { | ||
| _lookupTable.Add(newHashValue, 1); | ||
| } | ||
| } | ||
|
|
||
| public void Clear() | ||
| { | ||
| _counter = 0; | ||
| _values.Clear(); | ||
| _lookupTable.Clear(); | ||
| } | ||
| } | ||
| } | ||
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
18 changes: 18 additions & 0 deletions
18
...sualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/InfoBarService/IInfoBarService.cs
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 |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // 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.md file in the project root for more information. | ||
|
|
||
| using Microsoft.VisualStudio.Imaging.Interop; | ||
|
|
||
| namespace Microsoft.VisualStudio.ProjectSystem.VS.UI.InfoBarService; | ||
|
|
||
| /// <summary> | ||
| /// Displays messages in the information bar attached to Visual Studio's main window. | ||
| /// </summary> | ||
| [ProjectSystemContract(ProjectSystemContractScope.ProjectService, ProjectSystemContractProvider.System)] | ||
| internal interface IInfoBarService | ||
| { | ||
| /// <summary> | ||
| /// Shows an information bar with the specified message, image and UI, replacing an existing one | ||
| /// with the same message if there is one. | ||
| /// </summary> | ||
| Task ShowInfoBarAsync(string message, ImageMoniker image, CancellationToken cancellationToken, params InfoBarUI[] items); | ||
| } |
47 changes: 47 additions & 0 deletions
47
...oft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/InfoBarService/InfoBarUI.cs
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 |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // 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.md file in the project root for more information. | ||
|
|
||
| namespace Microsoft.VisualStudio.ProjectSystem.VS.UI.InfoBarService; | ||
|
|
||
| /// <summary> | ||
| /// Represents information bar user interface elements. | ||
| /// </summary> | ||
| internal class InfoBarUI | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="InfoBarUI"/> class. | ||
| /// </summary> | ||
| /// <param name="title">The title of the UI _element.</param> | ||
| /// <param name="kind">The kind of the UI _element, either <see cref="InfoBarUIKind.Button"/> or <see cref="InfoBarUIKind.Hyperlink"/></param> | ||
| /// <param name="action">The <see cref="Action"/> to run when the UI _element is clicked</param> | ||
| /// <param name="closeAfterAction"><see langword="true"/> if the information bar should close after <paramref name="action"/> is run.</param> | ||
| public InfoBarUI(string title, InfoBarUIKind kind, Action action, bool closeAfterAction = true) | ||
| { | ||
| Requires.NotNullOrEmpty(title, nameof(title)); | ||
| Requires.NotNull(action, nameof(action)); | ||
|
|
||
| Title = title; | ||
| Kind = kind; | ||
| Action = action; | ||
| CloseAfterAction = closeAfterAction; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the title of the UI _element. | ||
| /// </summary> | ||
| public string Title { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the kind of the UI _element, either <see cref="InfoBarUIKind.Button"/> or <see cref="InfoBarUIKind.Hyperlink"/>. | ||
| /// </summary> | ||
| public InfoBarUIKind Kind { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the <see cref="Action"/> to run when the UI _element is clicked. | ||
| /// </summary> | ||
| public Action Action { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets whether information bar should close after <see cref="Action"/> is run. | ||
| /// </summary> | ||
| public bool CloseAfterAction { get; } | ||
| } |
19 changes: 19 additions & 0 deletions
19
...VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/UI/InfoBarService/InfoBarUIKind.cs
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 |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // 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.md file in the project root for more information. | ||
|
|
||
| namespace Microsoft.VisualStudio.ProjectSystem.VS.UI.InfoBarService; | ||
|
|
||
| /// <summary> | ||
| /// Represents the kind of a <see cref="InfoBarUI"/>. | ||
| /// </summary> | ||
| internal enum InfoBarUIKind | ||
| { | ||
| /// <summary> | ||
| /// The <see cref="InfoBarUI"/> is a button. | ||
| /// </summary> | ||
| Button, | ||
|
|
||
| /// <summary> | ||
| /// The <see cref="InfoBarUI"/> is a hyperlink. | ||
| /// </summary> | ||
| Hyperlink, | ||
| } |
60 changes: 60 additions & 0 deletions
60
...jectSystem.Managed.VS/ProjectSystem/VS/UI/InfoBarService/VsInfoBarService.InfoBarEntry.cs
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 |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| // 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.md file in the project root for more information. | ||
|
|
||
| using Microsoft.VisualStudio.Shell.Interop; | ||
|
|
||
| namespace Microsoft.VisualStudio.ProjectSystem.VS.UI.InfoBarService; | ||
|
|
||
| internal partial class VsInfoBarService | ||
| { | ||
| private class InfoBarEntry : IVsInfoBarUIEvents | ||
| { | ||
| private readonly IVsInfoBarUIElement _element; | ||
| private readonly InfoBarUI[] _items; | ||
| private readonly Action<InfoBarEntry> _onClose; | ||
| private readonly uint _cookie; | ||
|
|
||
| public InfoBarEntry(string message, IVsInfoBarUIElement element, InfoBarUI[] items, Action<InfoBarEntry> onClose) | ||
| { | ||
| Assumes.NotNull(message); | ||
| Assumes.NotNull(element); | ||
| Assumes.NotNull(items); | ||
| Assumes.NotNull(onClose); | ||
|
|
||
| Message = message; | ||
|
ocallesp marked this conversation as resolved.
Outdated
|
||
| _element = element; | ||
| _items = items; | ||
| _onClose = onClose; | ||
| element.Advise(this, out _cookie); | ||
| } | ||
|
|
||
| public string Message { get; } | ||
|
|
||
| public void Close() | ||
| { | ||
| _element.Close(); | ||
| } | ||
|
|
||
| public void OnActionItemClicked(IVsInfoBarUIElement element, IVsInfoBarActionItem actionItem) | ||
| { | ||
| Requires.NotNull(element, nameof(element)); | ||
| Requires.NotNull(actionItem, nameof(actionItem)); | ||
|
|
||
| InfoBarUI item = _items.First(i => i.Title == actionItem.Text); | ||
| item.Action(); | ||
|
|
||
| if (item.CloseAfterAction) | ||
| { | ||
| Close(); | ||
| } | ||
| } | ||
|
|
||
| public void OnClosed(IVsInfoBarUIElement element) | ||
| { | ||
| Requires.NotNull(element, nameof(element)); | ||
|
|
||
| _element.Unadvise(_cookie); | ||
| _onClose(this); | ||
| } | ||
| } | ||
| } | ||
|
|
||
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.
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.
Why are we using a queue here and not some data structure with O(1) lookup? I expected this to be a hash table of hashes. A queue doesn't make a lot of sense in my mind. Am I missing some info? #Closed
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.
I added the hash _lookupTable to improve the search to O(1)
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.
I'm still not convinced a queue is the correct data structure here. Can you please call me to discuss as requested?