From 828268f6da2bd4db33e76582225a37aa8c1142d4 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Tue, 19 Jun 2018 18:01:59 +0200 Subject: [PATCH 01/14] add concurrent access detection tests --- ...ts.ConcurrentAccessDetection.netcoreapp.cs | 177 ++++++++++++++++++ .../tests/System.Collections.Tests.csproj | 1 + 2 files changed, 178 insertions(+) create mode 100644 src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs new file mode 100644 index 000000000000..ad12690ba7d8 --- /dev/null +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -0,0 +1,177 @@ +// 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 file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using Common.System; +using Xunit; + +namespace Generic.Dictionary +{ + public class DictionaryConcurrentAccessDetectionTests + { + [Fact] + public static void Add_DictionaryConcurrentAccessDetection_NullComparer_ValueTypeKey() + { + Thread customThread = new Thread(() => + { + Dictionary dic = new Dictionary(); + dic.Add(1, 1); + + //break internal state + var entriesType = dic.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); + var entriesInstance = (Array)entriesType.GetValue(dic); + var field = entriesInstance.GetType().GetElementType(); + var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { dic.Count }); + var entry = Activator.CreateInstance(field); + entriesType.SetValue(dic, entryArray); + + Assert.Null(dic.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dic)); + Assert.True(dic.GetType().GetGenericArguments()[0].IsValueType); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Add(1, 1)).TargetSite.Name); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[1]).TargetSite.Name); + //Remove is not resilient yet + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1)).TargetSite.Name); + }); + customThread.IsBackground = true; + customThread.Start(); + + //Wait max 5 seconds, could loop forever + Assert.True(customThread.Join(TimeSpan.FromSeconds(5))); + } + + [Fact] + public static void Add_DictionaryConcurrentAccessDetection_Comparer_ValueTypeKey() + { + Thread customThread = new Thread(() => + { + Dictionary dic = new Dictionary(new CustomEqualityComparerInt32ValueType()); + dic.Add(1, 1); + + //break internal state + var entriesType = dic.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); + var entriesInstance = (Array)entriesType.GetValue(dic); + var field = entriesInstance.GetType().GetElementType(); + var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { dic.Count }); + var entry = Activator.CreateInstance(field); + entriesType.SetValue(dic, entryArray); + + Assert.NotNull(dic.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dic)); + Assert.True(dic.GetType().GetGenericArguments()[0].IsValueType); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Add(1, 1)).TargetSite.Name); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[1]).TargetSite.Name); + //Remove is not resilient yet + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1)).TargetSite.Name); + }); + customThread.IsBackground = true; + customThread.Start(); + + //Wait max 5 seconds, could loop forever + Assert.True(customThread.Join(TimeSpan.FromSeconds(5))); + } + + [Fact] + public static void Add_DictionaryConcurrentAccessDetection_NullComparer_ReferenceTypeKey() + { + Thread customThread = new Thread(() => + { + Dictionary dic = new Dictionary(); + dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 }); + + //break internal state + var entriesType = dic.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); + var entriesInstance = (Array)entriesType.GetValue(dic); + var field = entriesInstance.GetType().GetElementType(); + var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { dic.Count }); + var entry = Activator.CreateInstance(field); + entriesType.SetValue(dic, entryArray); + + Assert.Null(dic.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dic)); + Assert.False(dic.GetType().GetGenericArguments()[0].IsValueType); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 })).TargetSite.Name); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[new DummyRefType() { Value = 1 }]).TargetSite.Name); + //Remove is not resilient yet + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 })).TargetSite.Name); + }); + customThread.IsBackground = true; + customThread.Start(); + + //Wait max 5 seconds, could loop forever + Assert.True(customThread.Join(TimeSpan.FromSeconds(5))); + } + + [Fact] + public static void Add_DictionaryConcurrentAccessDetection_Comparer_ReferenceTypeKey() + { + Thread customThread = new Thread(() => + { + Dictionary dic = new Dictionary(new CustomEqualityComparerDummyRefType()); + dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 }); + + //break internal state + var entriesType = dic.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); + var entriesInstance = (Array)entriesType.GetValue(dic); + var field = entriesInstance.GetType().GetElementType(); + var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { dic.Count }); + var entry = Activator.CreateInstance(field); + entriesType.SetValue(dic, entryArray); + + Assert.NotNull(dic.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dic)); + Assert.False(dic.GetType().GetGenericArguments()[0].IsValueType); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 })).TargetSite.Name); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[new DummyRefType() { Value = 1 }]).TargetSite.Name); + //Remove is not resilient yet + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 })).TargetSite.Name); + }); + customThread.IsBackground = true; + customThread.Start(); + + //Wait max 5 seconds, could loop forever + Assert.True(customThread.Join(TimeSpan.FromSeconds(5))); + } + } + + public class DummyRefType + { + public int Value { get; set; } + public override bool Equals(object obj) + { + return ((DummyRefType)obj).Equals(this.Value); + } + public override int GetHashCode() + { + return Value.GetHashCode(); + } + } + + public class CustomEqualityComparerDummyRefType : EqualityComparer + { + public override bool Equals(DummyRefType x, DummyRefType y) + { + return x.Value == y.Value; + } + + public override int GetHashCode(DummyRefType obj) + { + return obj.GetHashCode(); + } + } + + public class CustomEqualityComparerInt32ValueType : EqualityComparer + { + public override bool Equals(int x, int y) + { + return EqualityComparer.Default.Equals(x, y); + } + + public override int GetHashCode(int obj) + { + return EqualityComparer.Default.GetHashCode(obj); + } + } +} + diff --git a/src/System.Collections/tests/System.Collections.Tests.csproj b/src/System.Collections/tests/System.Collections.Tests.csproj index 40039d447f5d..ede741e17a0c 100644 --- a/src/System.Collections/tests/System.Collections.Tests.csproj +++ b/src/System.Collections/tests/System.Collections.Tests.csproj @@ -78,6 +78,7 @@ + From 41cdfc8dadd86ae168f729ba5c99de30c516fe95 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Tue, 19 Jun 2018 18:06:49 +0200 Subject: [PATCH 02/14] add remove overloads --- ...nary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index ad12690ba7d8..661c9e984806 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -36,6 +36,7 @@ public static void Add_DictionaryConcurrentAccessDetection_NullComparer_ValueTyp Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[1]).TargetSite.Name); //Remove is not resilient yet //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1)).TargetSite.Name); + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1, out int value)).TargetSite.Name); }); customThread.IsBackground = true; customThread.Start(); @@ -66,6 +67,7 @@ public static void Add_DictionaryConcurrentAccessDetection_Comparer_ValueTypeKey Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[1]).TargetSite.Name); //Remove is not resilient yet //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1)).TargetSite.Name); + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1, out int value)).TargetSite.Name); }); customThread.IsBackground = true; customThread.Start(); @@ -96,6 +98,7 @@ public static void Add_DictionaryConcurrentAccessDetection_NullComparer_Referenc Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[new DummyRefType() { Value = 1 }]).TargetSite.Name); //Remove is not resilient yet //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 })).TargetSite.Name); + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 }, out DummyRefType value)).TargetSite.Name); }); customThread.IsBackground = true; customThread.Start(); @@ -126,6 +129,7 @@ public static void Add_DictionaryConcurrentAccessDetection_Comparer_ReferenceTyp Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[new DummyRefType() { Value = 1 }]).TargetSite.Name); //Remove is not resilient yet //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 })).TargetSite.Name); + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 }, out DummyRefType value)).TargetSite.Name); }); customThread.IsBackground = true; customThread.Start(); From 15faf4117d4872386f4fe5eb0e47b0709a538249 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 20 Jun 2018 13:27:30 +0200 Subject: [PATCH 03/14] simplify tests --- ...ts.ConcurrentAccessDetection.netcoreapp.cs | 225 +++++++----------- 1 file changed, 86 insertions(+), 139 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index 661c9e984806..6978d344edc1 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -3,179 +3,126 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Threading; -using Common.System; +using System.Threading.Tasks; using Xunit; namespace Generic.Dictionary { public class DictionaryConcurrentAccessDetectionTests { - [Fact] - public static void Add_DictionaryConcurrentAccessDetection_NullComparer_ValueTypeKey() + async static Task DictionaryConcurrentAccessDetection(Dictionary dictionary, bool isValueType, object comparer, Action> add, Action> get, Action> remove, Action> removeOutParam) { - Thread customThread = new Thread(() => + Task task = Task.Factory.StartNew(() => { - Dictionary dic = new Dictionary(); - dic.Add(1, 1); - //break internal state - var entriesType = dic.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); - var entriesInstance = (Array)entriesType.GetValue(dic); + var entriesType = dictionary.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); + var entriesInstance = (Array)entriesType.GetValue(dictionary); var field = entriesInstance.GetType().GetElementType(); - var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { dic.Count }); + var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { ((IDictionary)dictionary).Count }); var entry = Activator.CreateInstance(field); - entriesType.SetValue(dic, entryArray); + entriesType.SetValue(dictionary, entryArray); - Assert.Null(dic.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dic)); - Assert.True(dic.GetType().GetGenericArguments()[0].IsValueType); - Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Add(1, 1)).TargetSite.Name); - Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[1]).TargetSite.Name); + Assert.Equal(comparer, dictionary.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dictionary)); + Assert.Equal(isValueType, dictionary.GetType().GetGenericArguments()[0].IsValueType); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => add(dictionary)).TargetSite.Name); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => get(dictionary)).TargetSite.Name); //Remove is not resilient yet - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1)).TargetSite.Name); - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1, out int value)).TargetSite.Name); - }); - customThread.IsBackground = true; - customThread.Start(); - - //Wait max 5 seconds, could loop forever - Assert.True(customThread.Join(TimeSpan.FromSeconds(5))); - } + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => remove(dictionary)).TargetSite.Name); + //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => removeOutParam(dictionary)).TargetSite.Name); + }, TaskCreationOptions.LongRunning); - [Fact] - public static void Add_DictionaryConcurrentAccessDetection_Comparer_ValueTypeKey() - { - Thread customThread = new Thread(() => - { - Dictionary dic = new Dictionary(new CustomEqualityComparerInt32ValueType()); - dic.Add(1, 1); - - //break internal state - var entriesType = dic.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); - var entriesInstance = (Array)entriesType.GetValue(dic); - var field = entriesInstance.GetType().GetElementType(); - var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { dic.Count }); - var entry = Activator.CreateInstance(field); - entriesType.SetValue(dic, entryArray); - - Assert.NotNull(dic.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dic)); - Assert.True(dic.GetType().GetGenericArguments()[0].IsValueType); - Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Add(1, 1)).TargetSite.Name); - Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[1]).TargetSite.Name); - //Remove is not resilient yet - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1)).TargetSite.Name); - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(1, out int value)).TargetSite.Name); - }); - customThread.IsBackground = true; - customThread.Start(); - - //Wait max 5 seconds, could loop forever - Assert.True(customThread.Join(TimeSpan.FromSeconds(5))); + //Wait max 60 seconds, could loop forever + Assert.True((await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(60))) == task) && task.IsCompletedSuccessfully); } - [Fact] - public static void Add_DictionaryConcurrentAccessDetection_NullComparer_ReferenceTypeKey() + [Theory] + [InlineData(null)] + [InlineData(typeof(CustomEqualityComparerInt32ValueType))] + async public static Task Add_DictionaryConcurrentAccessDetection_ValueTypeKey(Type comparerType) { - Thread customThread = new Thread(() => - { - Dictionary dic = new Dictionary(); - dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 }); - - //break internal state - var entriesType = dic.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); - var entriesInstance = (Array)entriesType.GetValue(dic); - var field = entriesInstance.GetType().GetElementType(); - var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { dic.Count }); - var entry = Activator.CreateInstance(field); - entriesType.SetValue(dic, entryArray); - - Assert.Null(dic.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dic)); - Assert.False(dic.GetType().GetGenericArguments()[0].IsValueType); - Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 })).TargetSite.Name); - Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[new DummyRefType() { Value = 1 }]).TargetSite.Name); - //Remove is not resilient yet - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 })).TargetSite.Name); - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 }, out DummyRefType value)).TargetSite.Name); - }); - customThread.IsBackground = true; - customThread.Start(); - - //Wait max 5 seconds, could loop forever - Assert.True(customThread.Join(TimeSpan.FromSeconds(5))); + IEqualityComparer customComparer = null; + + Dictionary dic = + comparerType == null ? + new Dictionary() : + new Dictionary((customComparer = (IEqualityComparer)Activator.CreateInstance(comparerType))); + + dic.Add(1, 1); + + await DictionaryConcurrentAccessDetection(dic, + typeof(int).IsValueType, + customComparer, + d => d.Add(1, 1), + d => { var v = d[1]; }, + d => d.Remove(1), + d => d.Remove(1, out int value)); } - [Fact] - public static void Add_DictionaryConcurrentAccessDetection_Comparer_ReferenceTypeKey() + [Theory] + [InlineData(null)] + [InlineData(typeof(CustomEqualityComparerDummyRefType))] + async public static Task Add_DictionaryConcurrentAccessDetection_ReferenceTypeKey(Type comparerType) { - Thread customThread = new Thread(() => - { - Dictionary dic = new Dictionary(new CustomEqualityComparerDummyRefType()); - dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 }); - - //break internal state - var entriesType = dic.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); - var entriesInstance = (Array)entriesType.GetValue(dic); - var field = entriesInstance.GetType().GetElementType(); - var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { dic.Count }); - var entry = Activator.CreateInstance(field); - entriesType.SetValue(dic, entryArray); - - Assert.NotNull(dic.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dic)); - Assert.False(dic.GetType().GetGenericArguments()[0].IsValueType); - Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 })).TargetSite.Name); - Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic[new DummyRefType() { Value = 1 }]).TargetSite.Name); - //Remove is not resilient yet - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 })).TargetSite.Name); - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => dic.Remove(new DummyRefType() { Value = 1 }, out DummyRefType value)).TargetSite.Name); - }); - customThread.IsBackground = true; - customThread.Start(); - - //Wait max 5 seconds, could loop forever - Assert.True(customThread.Join(TimeSpan.FromSeconds(5))); + IEqualityComparer customComparer = null; + + Dictionary dic = + comparerType == null ? + new Dictionary() : + new Dictionary((customComparer = (IEqualityComparer)Activator.CreateInstance(comparerType))); + + dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 }); + + await DictionaryConcurrentAccessDetection(dic, + typeof(DummyRefType).IsValueType, + customComparer, + d => d.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 }), + d => { var v = d[new DummyRefType() { Value = 1 }]; }, + d => d.Remove(new DummyRefType() { Value = 1 }), + d => d.Remove(new DummyRefType() { Value = 1 }, out DummyRefType value)); } } +} - public class DummyRefType +public class DummyRefType +{ + public int Value { get; set; } + public override bool Equals(object obj) { - public int Value { get; set; } - public override bool Equals(object obj) - { - return ((DummyRefType)obj).Equals(this.Value); - } - public override int GetHashCode() - { - return Value.GetHashCode(); - } + return ((DummyRefType)obj).Equals(this.Value); + } + public override int GetHashCode() + { + return Value.GetHashCode(); } +} - public class CustomEqualityComparerDummyRefType : EqualityComparer +public class CustomEqualityComparerDummyRefType : EqualityComparer +{ + public override bool Equals(DummyRefType x, DummyRefType y) { - public override bool Equals(DummyRefType x, DummyRefType y) - { - return x.Value == y.Value; - } + return x.Value == y.Value; + } - public override int GetHashCode(DummyRefType obj) - { - return obj.GetHashCode(); - } + public override int GetHashCode(DummyRefType obj) + { + return obj.GetHashCode(); } +} - public class CustomEqualityComparerInt32ValueType : EqualityComparer +public class CustomEqualityComparerInt32ValueType : EqualityComparer +{ + public override bool Equals(int x, int y) { - public override bool Equals(int x, int y) - { - return EqualityComparer.Default.Equals(x, y); - } + return EqualityComparer.Default.Equals(x, y); + } - public override int GetHashCode(int obj) - { - return EqualityComparer.Default.GetHashCode(obj); - } + public override int GetHashCode(int obj) + { + return EqualityComparer.Default.GetHashCode(obj); } } + From 83b864acf1edd27a6c909421fc7f51c9aa414f67 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 20 Jun 2018 14:51:47 +0200 Subject: [PATCH 04/14] address PR feedback --- ...ts.ConcurrentAccessDetection.netcoreapp.cs | 65 ++++++++++--------- .../tests/System.Collections.Tests.csproj | 30 +++++---- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index 6978d344edc1..a2014ed2ebbb 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -13,7 +13,7 @@ namespace Generic.Dictionary { public class DictionaryConcurrentAccessDetectionTests { - async static Task DictionaryConcurrentAccessDetection(Dictionary dictionary, bool isValueType, object comparer, Action> add, Action> get, Action> remove, Action> removeOutParam) + async Task DictionaryConcurrentAccessDetection(Dictionary dictionary, bool isValueType, object comparer, Action> add, Action> get, Action> remove, Action> removeOutParam) { Task task = Task.Factory.StartNew(() => { @@ -41,7 +41,7 @@ async static Task DictionaryConcurrentAccessDetection(Dictionary customComparer = null; @@ -64,7 +64,7 @@ await DictionaryConcurrentAccessDetection(dic, [Theory] [InlineData(null)] [InlineData(typeof(CustomEqualityComparerDummyRefType))] - async public static Task Add_DictionaryConcurrentAccessDetection_ReferenceTypeKey(Type comparerType) + public async Task DictionaryConcurrentAccessDetection_ReferenceTypeKey(Type comparerType) { IEqualityComparer customComparer = null; @@ -84,45 +84,48 @@ await DictionaryConcurrentAccessDetection(dic, d => d.Remove(new DummyRefType() { Value = 1 }, out DummyRefType value)); } } -} -public class DummyRefType -{ - public int Value { get; set; } - public override bool Equals(object obj) - { - return ((DummyRefType)obj).Equals(this.Value); - } - public override int GetHashCode() + class DummyRefType { - return Value.GetHashCode(); - } -} + public int Value { get; set; } + public override bool Equals(object obj) + { + return ((DummyRefType)obj).Equals(this.Value); + } -public class CustomEqualityComparerDummyRefType : EqualityComparer -{ - public override bool Equals(DummyRefType x, DummyRefType y) - { - return x.Value == y.Value; + public override int GetHashCode() + { + return Value.GetHashCode(); + } } - public override int GetHashCode(DummyRefType obj) + class CustomEqualityComparerDummyRefType : EqualityComparer { - return obj.GetHashCode(); - } -} + public override bool Equals(DummyRefType x, DummyRefType y) + { + return x.Value == y.Value; + } -public class CustomEqualityComparerInt32ValueType : EqualityComparer -{ - public override bool Equals(int x, int y) - { - return EqualityComparer.Default.Equals(x, y); + public override int GetHashCode(DummyRefType obj) + { + return obj.GetHashCode(); + } } - public override int GetHashCode(int obj) + class CustomEqualityComparerInt32ValueType : EqualityComparer { - return EqualityComparer.Default.GetHashCode(obj); + public override bool Equals(int x, int y) + { + return EqualityComparer.Default.Equals(x, y); + } + + public override int GetHashCode(int obj) + { + return EqualityComparer.Default.GetHashCode(obj); + } } } + + diff --git a/src/System.Collections/tests/System.Collections.Tests.csproj b/src/System.Collections/tests/System.Collections.Tests.csproj index ede741e17a0c..8e1ec81bb0a1 100644 --- a/src/System.Collections/tests/System.Collections.Tests.csproj +++ b/src/System.Collections/tests/System.Collections.Tests.csproj @@ -22,15 +22,9 @@ Common\System\Collections\IDictionary.NonGeneric.Tests.cs - - Common\System\Collections\IDictionary.NonGeneric.Tests.netcoreapp.cs - Common\System\Collections\IDictionary.Generic.Tests.cs - - Common\System\Collections\IDictionary.Generic.Tests.netcoreapp.cs - Common\System\Collections\IEnumerable.NonGeneric.Tests.cs @@ -77,15 +71,11 @@ Common\System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs - - - - @@ -100,7 +90,6 @@ - @@ -132,7 +121,6 @@ - @@ -146,12 +134,10 @@ - - Common\System\Diagnostics\DebuggerAttributes.cs @@ -163,6 +149,22 @@ Common\System\Collections\IEnumerable.Generic.Serialization.Tests.cs + + + + + + + + + + + Common\System\Collections\IDictionary.Generic.Tests.netcoreapp.cs + + + Common\System\Collections\IDictionary.NonGeneric.Tests.netcoreapp.cs + + From f34e2c483b90dc01ec0c613913236d76a07ed827 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 20 Jun 2018 14:54:10 +0200 Subject: [PATCH 05/14] remove var --- ...neric.Tests.ConcurrentAccessDetection.netcoreapp.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index a2014ed2ebbb..d7446a1352a0 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -18,11 +18,11 @@ async Task DictionaryConcurrentAccessDetection(Dictionary { //break internal state - var entriesType = dictionary.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); - var entriesInstance = (Array)entriesType.GetValue(dictionary); - var field = entriesInstance.GetType().GetElementType(); - var entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { ((IDictionary)dictionary).Count }); - var entry = Activator.CreateInstance(field); + FieldInfo entriesType = dictionary.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); + object entriesInstance = (Array)entriesType.GetValue(dictionary); + Type field = entriesInstance.GetType().GetElementType(); + Array entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { ((IDictionary)dictionary).Count }); + object entry = Activator.CreateInstance(field); entriesType.SetValue(dictionary, entryArray); Assert.Equal(comparer, dictionary.GetType().GetField("_comparer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dictionary)); From e2d1c528594b8b4ee2396f11dd9ca79f504c89ea Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 20 Jun 2018 14:55:00 +0200 Subject: [PATCH 06/14] nit extraline --- ...nary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index d7446a1352a0..d4ed9bc564f2 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -125,7 +125,3 @@ public override int GetHashCode(int obj) } } } - - - - From 176d9f6d94cf330f6fad528ed36fc01130a90a37 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 20 Jun 2018 14:59:32 +0200 Subject: [PATCH 07/14] move comparison into same line --- ...ry.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index d4ed9bc564f2..8216491a3160 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -45,8 +45,7 @@ public async Task DictionaryConcurrentAccessDetection_ValueTypeKey(Type comparer { IEqualityComparer customComparer = null; - Dictionary dic = - comparerType == null ? + Dictionary dic = comparerType == null ? new Dictionary() : new Dictionary((customComparer = (IEqualityComparer)Activator.CreateInstance(comparerType))); @@ -68,8 +67,7 @@ public async Task DictionaryConcurrentAccessDetection_ReferenceTypeKey(Type comp { IEqualityComparer customComparer = null; - Dictionary dic = - comparerType == null ? + Dictionary dic = comparerType == null ? new Dictionary() : new Dictionary((customComparer = (IEqualityComparer)Activator.CreateInstance(comparerType))); From 19540a14f07a4c240ff9dda3f75061a31e519bc7 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 20 Jun 2018 15:27:58 +0200 Subject: [PATCH 08/14] cleanup unuseful instance --- ...onary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index 8216491a3160..d98d931cc5db 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -21,8 +21,7 @@ async Task DictionaryConcurrentAccessDetection(Dictionary Date: Wed, 20 Jun 2018 15:28:51 +0200 Subject: [PATCH 09/14] cleanup typo --- ...onary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index d98d931cc5db..abd252c31732 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -19,8 +19,7 @@ async Task DictionaryConcurrentAccessDetection(Dictionary Date: Wed, 20 Jun 2018 15:35:07 +0200 Subject: [PATCH 10/14] improve readability --- ...Tests.ConcurrentAccessDetection.netcoreapp.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index abd252c31732..8d5c06849c22 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -19,8 +19,8 @@ async Task DictionaryConcurrentAccessDetection(Dictionary() : new Dictionary((customComparer = (IEqualityComparer)Activator.CreateInstance(comparerType))); - dic.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 }); + var keyValueSample = new DummyRefType() { Value = 1 }; + + dic.Add(keyValueSample, keyValueSample); await DictionaryConcurrentAccessDetection(dic, typeof(DummyRefType).IsValueType, customComparer, - d => d.Add(new DummyRefType() { Value = 1 }, new DummyRefType() { Value = 1 }), - d => { var v = d[new DummyRefType() { Value = 1 }]; }, - d => d.Remove(new DummyRefType() { Value = 1 }), - d => d.Remove(new DummyRefType() { Value = 1 }, out DummyRefType value)); + d => d.Add(keyValueSample, keyValueSample), + d => { var v = d[keyValueSample]; }, + d => d.Remove(keyValueSample), + d => d.Remove(keyValueSample, out DummyRefType value)); } } From 4ce87587c0e063915dc8a9ab5e1bf1a752e1a0aa Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Wed, 20 Jun 2018 16:32:33 +0200 Subject: [PATCH 11/14] address PR feedback --- ...neric.Tests.ConcurrentAccessDetection.netcoreapp.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index 8d5c06849c22..0eb6302a4d2f 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -17,9 +17,9 @@ async Task DictionaryConcurrentAccessDetection(Dictionary { - //break internal state + // set different entries array to simulate a dictionary corrupted by concurrent access FieldInfo entriesType = dictionary.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); - object entriesInstance = (Array)entriesType.GetValue(dictionary); + Array entriesInstance = (Array)entriesType.GetValue(dictionary); Array entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { ((IDictionary)dictionary).Count }); entriesType.SetValue(dictionary, entryArray); @@ -27,12 +27,12 @@ async Task DictionaryConcurrentAccessDetection(Dictionary(() => add(dictionary)).TargetSite.Name); Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => get(dictionary)).TargetSite.Name); - //Remove is not resilient yet + // Removes are not resilient yet //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => remove(dictionary)).TargetSite.Name); //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => removeOutParam(dictionary)).TargetSite.Name); }, TaskCreationOptions.LongRunning); - //Wait max 60 seconds, could loop forever + // Wait max 60 seconds, could loop forever Assert.True((await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(60))) == task) && task.IsCompletedSuccessfully); } @@ -83,6 +83,8 @@ await DictionaryConcurrentAccessDetection(dic, } } + // We use a custom type instead of string because string use optimized comparer https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/shared/System/Collections/Generic/Dictionary.cs#L79 + // We want to test case with _comparer = null class DummyRefType { public int Value { get; set; } From e04d27aa8629b0e58272a9d3bee92359e6af6440 Mon Sep 17 00:00:00 2001 From: danmosemsft Date: Wed, 20 Jun 2018 11:11:34 -0700 Subject: [PATCH 12/14] Comment --- ...y.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index 0eb6302a4d2f..70ee275f5d34 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -17,7 +17,10 @@ async Task DictionaryConcurrentAccessDetection(Dictionary { - // set different entries array to simulate a dictionary corrupted by concurrent access + // Get the Dictionary into a corrupted state, as if it had been corrupted by concurrent access. + // We this deterministically by clearing the _entries array using reflection; + // this means that every Entry struct has a 'next' field of zero, which causes the infinite loop + // that we want Dictionary to break out of FieldInfo entriesType = dictionary.GetType().GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance); Array entriesInstance = (Array)entriesType.GetValue(dictionary); Array entryArray = (Array)Activator.CreateInstance(entriesInstance.GetType(), new object[] { ((IDictionary)dictionary).Count }); @@ -32,7 +35,7 @@ async Task DictionaryConcurrentAccessDetection(Dictionary(() => removeOutParam(dictionary)).TargetSite.Name); }, TaskCreationOptions.LongRunning); - // Wait max 60 seconds, could loop forever + // If Dictionary regresses, we do not want to hang here indefinitely Assert.True((await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(60))) == task) && task.IsCompletedSuccessfully); } From 902738af357c7309eff7907f72c7b81ea9166948 Mon Sep 17 00:00:00 2001 From: danmosemsft Date: Wed, 20 Jun 2018 11:13:08 -0700 Subject: [PATCH 13/14] Access modifier --- ...ionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index 70ee275f5d34..b3c4c0893f9c 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -13,7 +13,7 @@ namespace Generic.Dictionary { public class DictionaryConcurrentAccessDetectionTests { - async Task DictionaryConcurrentAccessDetection(Dictionary dictionary, bool isValueType, object comparer, Action> add, Action> get, Action> remove, Action> removeOutParam) + private async Task DictionaryConcurrentAccessDetection(Dictionary dictionary, bool isValueType, object comparer, Action> add, Action> get, Action> remove, Action> removeOutParam) { Task task = Task.Factory.StartNew(() => { From ec9d5dfea91dc4333f2339c8e2675db8f768bc95 Mon Sep 17 00:00:00 2001 From: Marco Rossignoli Date: Thu, 21 Jun 2018 10:48:56 +0200 Subject: [PATCH 14/14] enable removes tests --- ...ary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs index b3c4c0893f9c..d14a965c26c7 100644 --- a/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs +++ b/src/System.Collections/tests/Generic/Dictionary/Dictionary.Generic.Tests.ConcurrentAccessDetection.netcoreapp.cs @@ -30,9 +30,8 @@ private async Task DictionaryConcurrentAccessDetection(Dictionary< Assert.Equal(isValueType, dictionary.GetType().GetGenericArguments()[0].IsValueType); Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => add(dictionary)).TargetSite.Name); Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => get(dictionary)).TargetSite.Name); - // Removes are not resilient yet - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => remove(dictionary)).TargetSite.Name); - //Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => removeOutParam(dictionary)).TargetSite.Name); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => remove(dictionary)).TargetSite.Name); + Assert.Equal("ThrowInvalidOperationException_ConcurrentOperationsNotSupported", Assert.Throws(() => removeOutParam(dictionary)).TargetSite.Name); }, TaskCreationOptions.LongRunning); // If Dictionary regresses, we do not want to hang here indefinitely