diff --git a/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs b/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs index 2535cbed080..7014fbcf960 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DictionarySerializerBase.cs @@ -420,8 +420,8 @@ public DictionarySerializerBase(DictionaryRepresentation dictionaryRepresentatio public DictionarySerializerBase(DictionaryRepresentation dictionaryRepresentation, IBsonSerializerRegistry serializerRegistry) : this( dictionaryRepresentation, - new Lazy>(() => serializerRegistry.GetSerializer()), - new Lazy>(() => serializerRegistry.GetSerializer())) + Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()), + Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer())) { if (serializerRegistry == null) { diff --git a/src/MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs b/src/MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs index 5d810b01861..ed67a89f23b 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/EnumerableSerializerBase.cs @@ -64,7 +64,7 @@ protected EnumerableSerializerBase(IBsonSerializerRegistry serializerRegistry) throw new ArgumentNullException("serializerRegistry"); } - _lazyItemSerializer = new Lazy(() => serializerRegistry.GetSerializer(typeof(object))); + _lazyItemSerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer(typeof(object))); } /// @@ -263,7 +263,7 @@ protected EnumerableSerializerBase(IBsonSerializerRegistry serializerRegistry) throw new ArgumentNullException("serializerRegistry"); } - _lazyItemSerializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItemSerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties diff --git a/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs index ca9ec6a771b..6fa8a4b4f15 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/IEnumerableDeserializingAsCollectionSerializer.cs @@ -81,7 +81,7 @@ public IEnumerableDeserializingAsCollectionSerializer(IBsonSerializerRegistry se throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItemSerializer = new Lazy>(serializerRegistry.GetSerializer); + _lazyItemSerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties diff --git a/src/MongoDB.Bson/Serialization/Serializers/ImpliedImplementationInterfaceSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ImpliedImplementationInterfaceSerializer.cs index fc8cdf81829..347bcda431e 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ImpliedImplementationInterfaceSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ImpliedImplementationInterfaceSerializer.cs @@ -74,7 +74,7 @@ public ImpliedImplementationInterfaceSerializer(IBsonSerializer /// /// The serializer registry. public ImpliedImplementationInterfaceSerializer(IBsonSerializerRegistry serializerRegistry) - : this(new Lazy>(() => serializerRegistry.GetSerializer())) + : this(Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer())) { if (serializerRegistry == null) { diff --git a/src/MongoDB.Bson/Serialization/Serializers/KeyValuePairSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/KeyValuePairSerializer.cs index 1ff39685b2a..0828a1c62f8 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/KeyValuePairSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/KeyValuePairSerializer.cs @@ -124,8 +124,8 @@ public KeyValuePairSerializer(BsonType representation, IBsonSerializer key public KeyValuePairSerializer(BsonType representation, IBsonSerializerRegistry serializerRegistry) : this( representation, - new Lazy>(() => serializerRegistry.GetSerializer()), - new Lazy>(() => serializerRegistry.GetSerializer())) + Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()), + Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer())) { if (serializerRegistry == null) { diff --git a/src/MongoDB.Bson/Serialization/Serializers/Lazy.cs b/src/MongoDB.Bson/Serialization/Serializers/Lazy.cs new file mode 100644 index 00000000000..63ea3f90cea --- /dev/null +++ b/src/MongoDB.Bson/Serialization/Serializers/Lazy.cs @@ -0,0 +1,33 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; + +namespace MongoDB.Bson.Serialization.Serializers; + +internal static class Lazy +{ + /// + /// Creates a with . + /// + /// The type of the lazily initialized value. + /// The factory delegate that produces the value. + /// A configured with . + public static Lazy CreatePublicationOnly(Func valueFactory) + { + return new Lazy(valueFactory, LazyThreadSafetyMode.PublicationOnly); + } +} diff --git a/src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs index 423b9500bed..893230a7b6d 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/NullableSerializer.cs @@ -160,7 +160,7 @@ public NullableSerializer(IBsonSerializerRegistry serializerRegistry) throw new ArgumentNullException("serializerRegistry"); } - _lazySerializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazySerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties diff --git a/src/MongoDB.Bson/Serialization/Serializers/SerializeAsNominalTypeSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/SerializeAsNominalTypeSerializer.cs index 065ae3873f9..e77e729b6e7 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/SerializeAsNominalTypeSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/SerializeAsNominalTypeSerializer.cs @@ -61,7 +61,7 @@ public SerializeAsNominalTypeSerializer(IBsonSerializerRegistry serializerRegist throw new ArgumentNullException("serializerRegistry"); } - _lazyNominalTypeSerializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyNominalTypeSerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public methods diff --git a/src/MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs index c2aaf3998fb..7c4be4d14e2 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ThreeDimensionalArraySerializer.cs @@ -63,7 +63,7 @@ public ThreeDimensionalArraySerializer(IBsonSerializerRegistry serializerRegistr throw new ArgumentNullException("serializerRegistry"); } - _lazyItemSerializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItemSerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties diff --git a/src/MongoDB.Bson/Serialization/Serializers/TupleSerializers.cs b/src/MongoDB.Bson/Serialization/Serializers/TupleSerializers.cs index a5b1e5f290a..884985bbb38 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/TupleSerializers.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/TupleSerializers.cs @@ -138,7 +138,7 @@ public TupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -236,8 +236,8 @@ public TupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException("serializerRegistry"); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -350,9 +350,9 @@ public TupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -479,10 +479,10 @@ public TupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -625,11 +625,11 @@ public TupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem5Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem5Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -787,12 +787,12 @@ public TupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException("serializerRegistry"); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem5Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem6Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem5Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem6Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -965,13 +965,13 @@ public TupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem5Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem6Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem7Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem5Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem6Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem7Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -1159,14 +1159,14 @@ public TupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem5Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem6Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem7Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyRestSerializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem5Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem6Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem7Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyRestSerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties diff --git a/src/MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs index afe701dd1c3..549d71237e9 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/TwoDimensionalArraySerializer.cs @@ -63,7 +63,7 @@ public TwoDimensionalArraySerializer(IBsonSerializerRegistry serializerRegistry) throw new ArgumentNullException("serializerRegistry"); } - _lazyItemSerializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItemSerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties diff --git a/src/MongoDB.Bson/Serialization/Serializers/ValueTupleSerializers.cs b/src/MongoDB.Bson/Serialization/Serializers/ValueTupleSerializers.cs index d0f52484e58..81cf5d75246 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/ValueTupleSerializers.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/ValueTupleSerializers.cs @@ -99,7 +99,7 @@ public ValueTupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -221,8 +221,8 @@ public ValueTupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -361,9 +361,9 @@ public ValueTupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -519,10 +519,10 @@ public ValueTupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -695,11 +695,11 @@ public ValueTupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem5Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem5Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -889,12 +889,12 @@ public ValueTupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem5Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem6Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem5Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem6Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -1101,13 +1101,13 @@ public ValueTupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem5Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem6Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem7Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem5Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem6Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem7Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties @@ -1332,14 +1332,14 @@ public ValueTupleSerializer(IBsonSerializerRegistry serializerRegistry) { if (serializerRegistry == null) { throw new ArgumentNullException(nameof(serializerRegistry)); } - _lazyItem1Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem2Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem3Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem4Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem5Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem6Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyItem7Serializer = new Lazy>(() => serializerRegistry.GetSerializer()); - _lazyRestSerializer = new Lazy>(() => serializerRegistry.GetSerializer()); + _lazyItem1Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem2Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem3Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem4Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem5Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem6Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyItem7Serializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); + _lazyRestSerializer = Lazy.CreatePublicationOnly(() => serializerRegistry.GetSerializer()); } // public properties diff --git a/tests/MongoDB.Bson.Tests/Serialization/BsonClassMapConcurrencyTests.cs b/tests/MongoDB.Bson.Tests/Serialization/BsonClassMapConcurrencyTests.cs new file mode 100644 index 00000000000..23bde5648d4 --- /dev/null +++ b/tests/MongoDB.Bson.Tests/Serialization/BsonClassMapConcurrencyTests.cs @@ -0,0 +1,96 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Options; +using Xunit; + +namespace MongoDB.Bson.Tests.Serialization +{ + public class BsonClassMapConcurrencyTests + { + [Fact] + public void LookupClassMap_should_not_deadlock_when_serializer_resolution_triggers_nested_class_map_lookup() + { + var mre1 = new ManualResetEventSlim(false); + var mre2 = new ManualResetEventSlim(false); + + DeadlockTriggeringOptionsAttribute.__mre1 = mre1; + DeadlockTriggeringOptionsAttribute.__mre2 = mre2; + + var taskB = Task.Run(() => BsonClassMap.LookupClassMap(typeof(ClassB))); + + mre1.Wait(5000); // Wait until taskB acquires the lock on Lazy>._state + + var taskA = Task.Run(() => BsonClassMap.LookupClassMap(typeof(ClassC))); + + Thread.Sleep(2000); // Wait until taskA acquires write-lock on BsonSerializer.ConfigLock + + mre2.Set(); // Release taskB + + var completed = Task.WhenAll(taskA, taskB).Wait(TimeSpan.FromSeconds(10)); + + completed.Should().BeTrue("LookupClassMap has deadlocked"); + } + + // nested types + private class ClassA + { + [DeadlockTriggeringOptions] + public int X { get; set; } + } + + private class ClassB + { + [BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)] + public Dictionary Dictionary { get; set; } = new(); + } + + private class ClassC : ClassB + { + } + + private class DeadlockTriggeringOptionsAttribute : BsonSerializationOptionsAttribute + { + internal static ManualResetEventSlim __mre1; + internal static ManualResetEventSlim __mre2; + + protected override IBsonSerializer Apply(IBsonSerializer serializer) + { + var mre1 = Interlocked.Exchange(ref __mre1, null); + var mre2 = Interlocked.Exchange(ref __mre2, null); + + mre1?.Set(); // Signal that taskB has acquired the lock on Lazy>._state + + if (mre2 != null) + { + // Wait until taskA acquires write-lock on BsonSerializer.ConfigLock, but avoid unbounded blocking + if (!mre2.Wait(TimeSpan.FromSeconds(5))) + { + throw new TimeoutException("Timeout while waiting for BsonSerializer.ConfigLock in DeadlockTriggeringOptionsAttribute.Apply."); + } + } + + return serializer; + } + } + } +}