Repository: ignite Updated Branches: refs/heads/master d253c0249 -> eae6e3b9f
IGNITE-5931 .NET: Fix type registration race condition This closes #2553 Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/eae6e3b9 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/eae6e3b9 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/eae6e3b9 Branch: refs/heads/master Commit: eae6e3b9fd43b42fc9d74e61118800dc0f3f6f0c Parents: d253c02 Author: Pavel Tupitsyn <[email protected]> Authored: Wed Aug 30 18:35:05 2017 +0300 Committer: Pavel Tupitsyn <[email protected]> Committed: Wed Aug 30 18:35:05 2017 +0300 ---------------------------------------------------------------------- .../Binary/BinaryDynamicRegistrationTest.cs | 49 ++++++++++++++++++++ .../Binary/BinarySelfTest.cs | 12 ----- .../Cache/Affinity/AffinityFieldTest.cs | 10 +++- .../Impl/Binary/Marshaller.cs | 43 ++++++++++------- 4 files changed, 84 insertions(+), 30 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/eae6e3b9/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDynamicRegistrationTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDynamicRegistrationTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDynamicRegistrationTest.cs index 4f458f4..01804b7 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDynamicRegistrationTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinaryDynamicRegistrationTest.cs @@ -25,6 +25,8 @@ namespace Apache.Ignite.Core.Tests.Binary using System.Collections.Generic; using System.IO; using System.Linq; + using System.Threading; + using System.Threading.Tasks; using Apache.Ignite.Core.Binary; using Apache.Ignite.Core.Cache.Configuration; using Apache.Ignite.Core.Cache.Store; @@ -357,6 +359,53 @@ namespace Apache.Ignite.Core.Tests.Binary } /// <summary> + /// Tests registration in multiple threads. + /// </summary> + [Test] + public void TestRegistrationMultithreaded([Values(true, false)] bool useTypeName) + { + const int iterations = 50; + const int threads = 4; + + using (var ignite = Ignition.Start(TestUtils.GetTestConfiguration())) + { + var cache = ignite.CreateCache<int, int>("c").WithKeepBinary<int, IBinaryObject>(); + var bin = ignite.GetBinary(); + Func<Type, IBinaryObjectBuilder> getBuilder = x => + useTypeName ? bin.GetBuilder(x.FullName) : bin.GetBuilder(x); + + var types = new[] { typeof(Foo), typeof(Bar), typeof(Bin) }; + + foreach (var type in types) + { + var type0 = type; // Modified closure. + + for (var i = 0; i < iterations; i++) + { + var countdown = new CountdownEvent(threads); + + Action registerType = () => + { + countdown.Signal(); + Assert.IsTrue(countdown.Wait(5000)); + + var binObj = getBuilder(type0).SetIntField("x", 1).Build(); + cache[1] = binObj; + + Assert.AreEqual(binObj, cache[1]); + }; + + var tasks = Enumerable.Range(0, threads) + .Select(x => Task.Factory.StartNew(registerType)) + .ToArray(); + + Task.WaitAll(tasks); + } + } + } + } + + /// <summary> /// Tests the type registration. /// </summary> private static void Test(IIgnite ignite1, IIgnite ignite2) http://git-wip-us.apache.org/repos/asf/ignite/blob/eae6e3b9/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinarySelfTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinarySelfTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinarySelfTest.cs index e24dca0..4237eda 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinarySelfTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/BinarySelfTest.cs @@ -1534,18 +1534,6 @@ namespace Apache.Ignite.Core.Tests.Binary Assert.AreEqual(nDateArr, obj2.NDateArr); } - [Test] - public void TestBinaryConfigurationValidation() - { - var cfg = new BinaryConfiguration(typeof (PropertyType)) - { - Types = new[] {typeof(PropertyType).AssemblyQualifiedName} - }; - - // ReSharper disable once ObjectCreationAsStatement - Assert.Throws<BinaryObjectException>(() => new Marshaller(cfg)); - } - /// <summary> /// Tests the compact footer setting. /// </summary> http://git-wip-us.apache.org/repos/asf/ignite/blob/eae6e3b9/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFieldTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFieldTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFieldTest.cs index 31326b7..c3482bb 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFieldTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Cache/Affinity/AffinityFieldTest.cs @@ -76,7 +76,8 @@ namespace Apache.Ignite.Core.Tests.Cache.Affinity _cache1.Put(new CacheKeyAttrOverride(), string.Empty); // Verify - foreach (var type in new[] { typeof(CacheKey) , typeof(CacheKeyAttr), typeof(CacheKeyAttrOverride)}) + foreach (var type in new[] { typeof(CacheKey), typeof(CacheKeyAttr), + typeof(CacheKeyAttrDynamicRegistration), typeof(CacheKeyAttrOverride)}) { Assert.AreEqual("AffinityKey", _cache1.Ignite.GetBinary().GetBinaryType(type).AffinityKeyFieldName); Assert.AreEqual("AffinityKey", _cache2.Ignite.GetBinary().GetBinaryType(type).AffinityKeyFieldName); @@ -91,6 +92,7 @@ namespace Apache.Ignite.Core.Tests.Cache.Affinity { TestKeyLocation0((key, affKey) => new CacheKey {Key = key, AffinityKey = affKey}); TestKeyLocation0((key, affKey) => new CacheKeyAttr {Key = key, AffinityKey = affKey}); + TestKeyLocation0((key, affKey) => new CacheKeyAttrDynamicRegistration {Key = key, AffinityKey = affKey}); TestKeyLocation0((key, affKey) => new CacheKeyAttrOverride {Key = key, AffinityKey = affKey}); } @@ -190,6 +192,12 @@ namespace Apache.Ignite.Core.Tests.Cache.Affinity [AffinityKeyMapped] public int AffinityKey { get; set; } } + private class CacheKeyAttrDynamicRegistration + { + public int Key { get; set; } + [AffinityKeyMapped] public int AffinityKey { get; set; } + } + private class CacheKeyAttrOverride { [AffinityKeyMapped] public int Key { get; set; } http://git-wip-us.apache.org/repos/asf/ignite/blob/eae6e3b9/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs index 5ede542..a6d5517 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Marshaller.cs @@ -503,21 +503,28 @@ namespace Apache.Ignite.Core.Impl.Binary desc = desc == null ? new BinaryFullTypeDescriptor(type, typeId, typeName, true, _cfg.NameMapper, - _cfg.IdMapper, ser, false, null, BinaryUtils.IsIgniteEnum(type), registered) + _cfg.IdMapper, ser, false, GetAffinityKeyFieldNameFromAttribute(type), + BinaryUtils.IsIgniteEnum(type), registered) : new BinaryFullTypeDescriptor(desc, type, ser, registered); if (RegistrationDisabled) + { return desc; + } var typeKey = BinaryUtils.TypeKey(true, typeId); var desc0 = _idToDesc.GetOrAdd(typeKey, x => desc); - if (desc0.Type != null && desc0.Type.FullName != type.FullName) + if (desc0.Type != null && desc0.TypeName != typeName) + { ThrowConflictingTypeError(type, desc0.Type, typeId); + } desc0 = _typeNameToDesc.GetOrAdd(typeName, x => desc); - if (desc0.Type != null && desc0.Type.FullName != type.FullName) + if (desc0.Type != null && desc0.TypeName != typeName) + { ThrowConflictingTypeError(type, desc0.Type, typeId); + } _typeToDesc.Set(type, desc); @@ -652,34 +659,36 @@ namespace Apache.Ignite.Core.Impl.Binary bool keepDeserialized, IBinaryNameMapper nameMapper, IBinaryIdMapper idMapper, IBinarySerializerInternal serializer, string affKeyFieldName, bool isEnum) { + Debug.Assert(!string.IsNullOrEmpty(typeName)); + long typeKey = BinaryUtils.TypeKey(userType, typeId); BinaryFullTypeDescriptor conflictingType; - if (_idToDesc.TryGetValue(typeKey, out conflictingType)) + if (_idToDesc.TryGetValue(typeKey, out conflictingType) && conflictingType.TypeName != typeName) { - var type1 = conflictingType.Type != null - ? conflictingType.Type.AssemblyQualifiedName - : conflictingType.TypeName; - - var type2 = type != null ? type.AssemblyQualifiedName : typeName; - - ThrowConflictingTypeError(type1, type2, typeId); + ThrowConflictingTypeError(typeName, conflictingType.TypeName, typeId); } - if (userType && _typeNameToDesc.ContainsKey(typeName)) - throw new BinaryObjectException("Conflicting type name: " + typeName); - var descriptor = new BinaryFullTypeDescriptor(type, typeId, typeName, userType, nameMapper, idMapper, serializer, keepDeserialized, affKeyFieldName, isEnum); + if (RegistrationDisabled) + { + return descriptor; + } + if (type != null) - _typeToDesc.GetOrAdd(type, x => descriptor); + { + _typeToDesc.Set(type, descriptor); + } if (userType) - _typeNameToDesc.GetOrAdd(typeName, x => descriptor); + { + _typeNameToDesc.Set(typeName, descriptor); + } - _idToDesc.GetOrAdd(typeKey, _ => descriptor); + _idToDesc.Set(typeKey, descriptor); return descriptor; }
