From 2e4b02e34783ecc6d4596d9bdd4368df0b85f9ac Mon Sep 17 00:00:00 2001 From: James Kovacs Date: Wed, 24 Mar 2021 13:43:14 -0600 Subject: [PATCH 1/2] CSHARP-2450: Improved deserialization performance by switching from HashSet protected by a ReaderWriterLockSlim to a ConcurrentDictionary outside the ReaderWriterLockSlim. --- .../Serialization/BsonSerializer.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/BsonSerializer.cs b/src/MongoDB.Bson/Serialization/BsonSerializer.cs index 52dabe5e7f6..4082dea45f1 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializer.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializer.cs @@ -14,6 +14,7 @@ */ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -41,7 +42,7 @@ public static class BsonSerializer private static HashSet __discriminatedTypes = new HashSet(); private static BsonSerializerRegistry __serializerRegistry; private static TypeMappingSerializationProvider __typeMappingSerializationProvider; - private static HashSet __typesWithRegisteredKnownTypes = new HashSet(); + private static ConcurrentDictionary __typesWithRegisteredKnownTypes = new ConcurrentDictionary(); private static bool __useNullIdChecker = false; private static bool __useZeroIdChecker = false; @@ -679,23 +680,15 @@ public static void Serialize( // internal static methods internal static void EnsureKnownTypesAreRegistered(Type nominalType) { - __configLock.EnterReadLock(); - try - { - if (__typesWithRegisteredKnownTypes.Contains(nominalType)) - { - return; - } - } - finally + if (__typesWithRegisteredKnownTypes.ContainsKey(nominalType)) { - __configLock.ExitReadLock(); + return; } __configLock.EnterWriteLock(); try { - if (!__typesWithRegisteredKnownTypes.Contains(nominalType)) + if (!__typesWithRegisteredKnownTypes.ContainsKey(nominalType)) { // only call LookupClassMap for classes with a BsonKnownTypesAttribute #if NET452 @@ -709,7 +702,7 @@ internal static void EnsureKnownTypesAreRegistered(Type nominalType) LookupSerializer(nominalType); } - __typesWithRegisteredKnownTypes.Add(nominalType); + __typesWithRegisteredKnownTypes[nominalType] = nominalType; } } finally From 440c55997fd607232747b1b4fb2ddbaf8b6637ad Mon Sep 17 00:00:00 2001 From: James Kovacs Date: Mon, 29 Mar 2021 11:07:48 -0600 Subject: [PATCH 2/2] Incorporated PR feedback. --- src/MongoDB.Bson/Serialization/BsonSerializer.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/BsonSerializer.cs b/src/MongoDB.Bson/Serialization/BsonSerializer.cs index 4082dea45f1..0f493a00f21 100644 --- a/src/MongoDB.Bson/Serialization/BsonSerializer.cs +++ b/src/MongoDB.Bson/Serialization/BsonSerializer.cs @@ -42,7 +42,8 @@ public static class BsonSerializer private static HashSet __discriminatedTypes = new HashSet(); private static BsonSerializerRegistry __serializerRegistry; private static TypeMappingSerializationProvider __typeMappingSerializationProvider; - private static ConcurrentDictionary __typesWithRegisteredKnownTypes = new ConcurrentDictionary(); + // ConcurrentDictionary is being used as a concurrent set of Type. The values will always be null. + private static ConcurrentDictionary __typesWithRegisteredKnownTypes = new ConcurrentDictionary(); private static bool __useNullIdChecker = false; private static bool __useZeroIdChecker = false; @@ -702,7 +703,10 @@ internal static void EnsureKnownTypesAreRegistered(Type nominalType) LookupSerializer(nominalType); } - __typesWithRegisteredKnownTypes[nominalType] = nominalType; + // NOTE: The nominalType MUST be added to __typesWithRegisteredKnownTypes after all registration + // work is done to ensure that other threads don't access a partially registered nominalType + // when performing the initial check above outside the __config lock. + __typesWithRegisteredKnownTypes[nominalType] = null; } } finally