-
Notifications
You must be signed in to change notification settings - Fork 387
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
Added check for circular dependencies #3474
Conversation
Codecov Report
@@ Coverage Diff @@
## dev15.7.x #3474 +/- ##
=============================================
+ Coverage 68.29% 68.43% +0.13%
=============================================
Files 731 731
Lines 36675 36719 +44
Branches 2119 2120 +1
=============================================
+ Hits 25049 25128 +79
+ Misses 11215 11172 -43
- Partials 411 419 +8
Continue to review full report at Codecov.
|
@etbyrd is there a way to write a test for this case? |
@@ -55,6 +55,8 @@ internal class TargetedDependenciesSnapshot : ITargetedDependenciesSnapshot | |||
private Dictionary<string, bool> _unresolvedDescendantsMap | |||
= new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); | |||
|
|||
private HashSet<string> _seenDependencies = new HashSet<string>(); |
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.
private HashSet _seenDependencies = new HashSet(); [](start = 7, length = 67)
Why keep it global? It seems like implementation detail of FindUnresolvedDependenciesRecursive and don't have to stay in memory? We already keeping too much stuff in memory ...
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.
Good point. I will fix that
I know that tests for this method are missing here https://github.com/dotnet/project-system/blob/master/src/Microsoft.VisualStudio.ProjectSystem.Managed.VS.UnitTests/ProjectSystem/VS/Tree/Dependencies/Snapshot/TargetedDependenciesSnapshotTests.cs but i would expect them to be added with next changes and now looks like a good moment :) Refers to: src/Microsoft.VisualStudio.ProjectSystem.Managed.VS/ProjectSystem/VS/Tree/Dependencies/Snapshot/TargetedDependenciesSnapshot.cs:78 in 6a424b2. [](commit_id = 6a424b2, deletion_comment = False) |
@jmarolf @abpiskunov I will be adding tests for this - I am still worried about some edge cases |
@@ -55,6 +55,8 @@ internal class TargetedDependenciesSnapshot : ITargetedDependenciesSnapshot | |||
private Dictionary<string, bool> _unresolvedDescendantsMap | |||
= new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase); | |||
|
|||
|
|||
|
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.
Nit: remove this added blank space.
b24daa6
to
6a424b2
Compare
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.
89be799
to
9ccfcbc
Compare
@@ -573,6 +573,47 @@ public void TFromChanges_RemovedAndAddedChanges() | |||
Assert.True(snapshot.DependenciesWorld.ContainsKey(@"tfm1\xxx\addeddependency3")); | |||
} | |||
|
|||
/// <summary> | |||
/// Added because circular dependencies can cause stack overflows | |||
/// https://github.com/dotnet/project-system/pull/3474 |
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.
Link to #3374 instead please
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.
Done
/// <summary> | ||
/// Internal for testing. | ||
/// </summary> | ||
internal TargetedDependenciesSnapshot(IDictionary<string, IDependency> dependenciesWorld, IEnumerable<IDependency> topLevelDependencies) |
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 would rather make this constructor private, with in internal static
factory method with a clear name like CreateForUnitTestingOnly
or something.
//Checking here will prevent a stack overflow due to rechecking the same dependencies | ||
if (_dependenciesChildrenMap.ContainsKey(child.Id)) | ||
{ | ||
unresolved = false; |
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 had to think about why this is sufficient to break cycles. My conclusion is that we don't have to worry about the unresolved case, because unresolved dependencies wouldn't have any children, is that right?
break; | ||
} | ||
|
||
//If the dependency is already in the child map, it is resolved |
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.
Nit: We put a space after //
in comments.
topLevelDependencies: new List<IDependency>() { dependencyModelTop1 }); | ||
|
||
// verify it doesn't stack overflow | ||
previousSnapshot.CheckForUnresolvedDependencies(dependencyModelTop1); |
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.
Have you verified that this test fails without your fix?
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.
Yes, it fails without the fix.
topLevelDependencies: new List<IDependency>() { dependencyModelTop1 }); | ||
|
||
// verify it doesn't stack overflow | ||
previousSnapshot.CheckForUnresolvedDependencies(dependencyModelTop1); |
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 would prefer if we didn't have the failure case here be: crash the test harness. That is going to be hard to diagnose if we ever regress this.
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 agree. I am working a way around this but I wonder if it is a requirement for this fix to be added.
fcf4fb4
to
aea2b93
Compare
Customer scenario
In certain circumstances, there can be a circular dependency that creates a stack overflow when recursively checking for unresolved child dependencies, causing a complete VS crash.
Bugs this fixes:
https://devdiv.visualstudio.com/DevDiv/_workitems/edit/487858
#3374
Workarounds, if any
None
Risk
Low, there are some threading concerns that I am looking into to make sure that the Hashset is not shared - but I do not think that will be an issue because I am editing a private method and the class is instantiated when creating a snapshot.
Performance impact
Low. Just adding a HashSet.
Is this a regression from a previous update?
No
Root cause analysis:
There was no check that a dependency was not already resolved.
How was the bug found?
Customer feedback.