-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Deep clone TaskRegistry and Toolset #8973
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
Changes from all commits
625d3ca
16299fd
f2945cf
a1503e5
a20850a
8af7090
935e0df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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. | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using Microsoft.Build.Collections; | ||
| using Microsoft.Build.Execution; | ||
|
|
||
| namespace Microsoft.Build.BackEnd; | ||
|
|
||
| internal static class CloningExtensions | ||
| { | ||
| public static PropertyDictionary<ProjectPropertyInstance>? DeepClone( | ||
| this PropertyDictionary<ProjectPropertyInstance>? properties) | ||
| => properties == null ? null : new(properties.Select<ProjectPropertyInstance, ProjectPropertyInstance>(p => p.DeepClone())); | ||
|
Comment on lines
+14
to
+16
Member
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. The extra copies seem ok based on your results so far, but we could also consider using our ImmutableDictionary, our wrapper CopyOnWriteDictionary, or a similar type to get copy-on-write behavior and minimize allocations (since it seems modifications are rare). |
||
|
|
||
| public static Dictionary<TKey, TValue>? DeepClone<TKey, TValue>( | ||
| this IDictionary<TKey, TValue>? dictionary, | ||
| IEqualityComparer<TKey> comparer) where TKey : notnull | ||
| => dictionary.DeepClone(null, null, comparer); | ||
|
|
||
| public static Dictionary<TKey, TValue>? DeepClone<TKey, TValue>( | ||
| this IDictionary<TKey, TValue>? dictionary, | ||
| Func<TValue, TValue> valueClone, | ||
| IEqualityComparer<TKey> comparer) where TKey : notnull | ||
| => dictionary.DeepClone(null, valueClone, comparer); | ||
|
|
||
| public static Dictionary<TKey, TValue>? DeepClone<TKey, TValue>( | ||
| this IDictionary<TKey, TValue>? dictionary, | ||
| Func<TKey, TKey> keyClone, | ||
| IEqualityComparer<TKey> comparer) where TKey : notnull | ||
| => dictionary.DeepClone(keyClone, null, comparer); | ||
|
|
||
| public static Dictionary<TKey, TValue>? DeepClone<TKey, TValue>( | ||
| this IDictionary<TKey, TValue>? dictionary, | ||
| Func<TKey, TKey>? keyClone, | ||
| Func<TValue, TValue>? valueClone, | ||
| IEqualityComparer<TKey> comparer) where TKey : notnull | ||
| => dictionary?.ToDictionary( | ||
| p => (keyClone ?? Identity)(p.Key), | ||
| p => (valueClone ?? Identity)(p.Value), | ||
| comparer); | ||
|
|
||
| private static T Identity<T>(T value) => value; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -50,6 +50,9 @@ private SubToolset(ITranslator translator) | |
| ((ITranslatable)this).Translate(translator); | ||
| } | ||
|
|
||
| internal SubToolset DeepClone() | ||
| => new SubToolset(_subToolsetVersion, _properties?.DeepClone()); | ||
|
Member
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. What causes mutation of
Member
Author
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. 👍 Or possibly the SubToolset itself should be created immutable (with properties flipped to immutable) - as currently this is not a case |
||
|
|
||
| /// <summary> | ||
| /// VisualStudioVersion that corresponds to this subtoolset | ||
| /// </summary> | ||
|
|
||
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.
Was this assertion always wrong? Have we added mutability where we shouldn't have, or just "in a way that requires we change 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.
Unfortunately this is older then GH history so I can only provide guessing - and my guess is the ProjectInstance and owned object graph (including TaskRegistry) was always meant to be instantiated once and then used only sequantially. So immutability and deepclonning was never strongly followed - as it was mostly ok in majority of cases (a specific set of VS actions and conditions needs to be made - e.g. in-proc build or cancel and rebuild - to hit the unwanted sharing of TaskRegistry/Toolset).
TaskRegistry looks almost immutable, with exception of the internal matching caches, lazy initialized state of held TaskFactoryWrapper instances (the reason for the 3rd state corruption perf issue), plus something else (I forgot now) - so the path of DeepCopying turned out to be easier and safer than attempting to make TaskRegistry and Toolset truly immutable
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.
tl;dr; the added mutability shouldn't be added without adding deep cloning IMHO