diff --git a/Bson/Bson.csproj b/Bson/Bson.csproj index ad661bedb2d..da3e857e074 100644 --- a/Bson/Bson.csproj +++ b/Bson/Bson.csproj @@ -83,6 +83,7 @@ Properties\GlobalAssemblyInfo.cs + diff --git a/Bson/FastSingleton.cs b/Bson/FastSingleton.cs new file mode 100644 index 00000000000..f50486c2e12 --- /dev/null +++ b/Bson/FastSingleton.cs @@ -0,0 +1,251 @@ +/* Copyright 2010-2012 10gen 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.Reflection; +using System.Threading; + +namespace MongoDB.Bson +{ + /// + /// Fast singleton abstract base class. Enables runtime, static, and lazy binding. + /// + /// The singleton value type. + internal abstract class FastSingleton where TValue : class + { + // private static fields + private static ReaderWriterLockSlim __readerWriterLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); + private static Dictionary> __dictionary = new Dictionary>(); + + // public properties + /// + /// Gets the singleton value. + /// + public abstract TValue Value + { + get; + } + + /// + /// Gets An optional user-defined object that contains information about the singleton value. + /// + public abstract object State + { + get; + } + + // public static methods + /// + /// Resolves a nominal type to a singleton instance. + /// + /// The nominal type. + /// A singleton instance. + public static FastSingleton Lookup(Type nominalType) + { + FastSingleton value; + + __readerWriterLock.EnterUpgradeableReadLock(); + try + { + if (__dictionary.TryGetValue(nominalType, out value)) + { + return value; + } + + __readerWriterLock.EnterWriteLock(); + try + { + if (__dictionary.TryGetValue(nominalType, out value)) + { + return value; + } + + var genericType = typeof(FastSingleton<,>).MakeGenericType(typeof(TValue), nominalType); + + var instancePropertyInfo = genericType.GetProperty( + "Instance", + BindingFlags.Static | BindingFlags.Public); + + value = (FastSingleton)instancePropertyInfo.GetValue(null, null); + + __dictionary.Add(nominalType, value); + + return value; + } + finally + { + __readerWriterLock.ExitWriteLock(); + } + } + finally + { + __readerWriterLock.ExitUpgradeableReadLock(); + } + } + + // public methods + /// + /// Sets the singleton value. + /// + /// The singleton value + /// An optional user-defined object that contains information about the singleton value. + /// Function is atomic. + /// true if the singleton value was set; otherwise false. + public abstract bool TrySetValue(TValue value, object state); + + // protected classes + /// + /// Groups a singleton value and user-defined object so they can be set atomically. + /// + protected class ValueStateTuple + { + // private fields + private readonly TValue value; + private readonly object state; + + // constructors + /// + /// Initializes a new instance of the Tuple class. + /// + /// The singleton value + /// An optional user-defined object that contains information about the singleton value. + public ValueStateTuple(TValue value, object state) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + this.value = value; + this.state = state; + } + + // public properties + /// + /// Gets the singleton value. + /// + public TValue Value + { + get + { + return this.value; + } + } + + /// + /// Gets an optional user-defined object that contains information about the singleton value. + /// + public object State + { + get + { + return this.state; + } + } + } + } + + /// + /// Fast singleton implementation class + /// + /// The singleton value type. + /// The nominal type associated with the singleton value. + internal sealed class FastSingleton : FastSingleton where TValue : class + { + // private static fields + private static readonly FastSingleton __instance = new FastSingleton(); + private static ValueStateTuple __tuple; // volatile accesses + + // constructors + /// + /// Initializes a new instance of the FastSingleton<TValue, TNominal> class. + /// + /// Private because FastSingleton<TValue, TNominal> is a singleton. + private FastSingleton() + { + } + + /// + /// Gets the singleton instance of the FastSingleton<TValue, TNominal> type. + /// + /// Accessed by FastSingleton.Create + public static FastSingleton Instance + { + get + { + return __instance; + } + } + + /// + /// Gets the singleton value. + /// + public override TValue Value + { + get + { + // see implementation of Thread.VolatileRead(); + var tuple = __tuple; + + Thread.MemoryBarrier(); + + if (tuple == null) + { + return null; + } + + return tuple.Value; + } + } + + /// + /// Gets An optional user-defined object that contains information about the singleton value. + /// + public override object State + { + get + { + // see implementation of Thread.VolatileRead(); + var tuple = __tuple; + + Thread.MemoryBarrier(); + + if (tuple == null) + { + return null; + } + + return tuple.State; + } + } + + /// + /// Sets the singleton value. + /// + /// The singleton value + /// An optional user-defined object that contains information about the singleton value. + /// Function is atomic. + /// true if the singleton value was set; otherwise false. + public override bool TrySetValue(TValue value, object State) + { + var tuple = new ValueStateTuple(value, State); + + var originalTuple = Interlocked.CompareExchange(ref __tuple, tuple, null); + + return originalTuple == null; + } + } +} diff --git a/Bson/Serialization/BsonClassMap.cs b/Bson/Serialization/BsonClassMap.cs index 4b5b92c18a2..6b102315b3e 100644 --- a/Bson/Serialization/BsonClassMap.cs +++ b/Bson/Serialization/BsonClassMap.cs @@ -62,6 +62,7 @@ public abstract class BsonClassMap private bool _ignoreExtraElementsIsInherited = false; private BsonMemberMap _extraElementsMemberMap; private List _knownTypes = new List(); + private readonly FastSingleton _discriminatorConvention; // constructors /// @@ -74,6 +75,7 @@ protected BsonClassMap(Type classType) _conventions = LookupConventions(classType); _discriminator = classType.Name; _isAnonymous = IsAnonymousType(classType); + _discriminatorConvention = FastSingleton.Lookup(classType); } // public properties @@ -581,9 +583,35 @@ public BsonClassMap Freeze() return this; } + /// + /// Gets the discriminator convention for the member type. + /// + /// The discriminator convention for the member type. + internal IDiscriminatorConvention GetDiscriminatorConvention() + { + if (_discriminatorConvention.Value == null) + { + // LookupDiscriminatorConvention will populate _discriminatorConvention + BsonDefaultSerializer.LookupDiscriminatorConvention(_classType); + } + + return _discriminatorConvention.Value; + } + /// /// Gets a member map (only considers members declared in this class). /// + /// The MemberInfo. + /// The member map. + public BsonMemberMap GetMemberMap(MemberInfo memberInfo) + { + // can be called whether frozen or not + return _declaredMemberMaps.Find(m => m.MemberInfo == memberInfo); + } + + /// + /// Gets a member map. + /// /// The member name. /// The member map (or null if the member was not found). public BsonMemberMap GetMemberMap(string memberName) @@ -1485,7 +1513,7 @@ public void UnmapProperty(Expression> propertyLam UnmapMember(propertyLambda); } - // private methods + // private static methods private static MemberInfo GetMemberInfoFromLambda(Expression> memberLambda) { var body = memberLambda.Body; diff --git a/Bson/Serialization/BsonClassMapSerializer.cs b/Bson/Serialization/BsonClassMapSerializer.cs index 18bbb9c1d82..cb8e0422611 100755 --- a/Bson/Serialization/BsonClassMapSerializer.cs +++ b/Bson/Serialization/BsonClassMapSerializer.cs @@ -23,6 +23,7 @@ using System.Text.RegularExpressions; using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization.Conventions; using MongoDB.Bson.Serialization.Options; namespace MongoDB.Bson.Serialization @@ -131,7 +132,15 @@ public object Deserialize( bsonReader.ReadStartDocument(); var missingElementMemberMaps = new HashSet(classMap.AllMemberMaps); // make a copy! - var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); + IDiscriminatorConvention discriminatorConvention; + if (actualType == nominalType) + { + discriminatorConvention = classMap.GetDiscriminatorConvention(); + } + else + { + discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); + } while (bsonReader.ReadBsonType() != BsonType.EndOfDocument) { var elementName = bsonReader.ReadName(); @@ -443,7 +452,7 @@ private void DeserializeMember(BsonReader bsonReader, object obj, BsonMemberMap } else { - var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(nominalType); + var discriminatorConvention = memberMap.GetDiscriminatorConvention(); actualType = discriminatorConvention.GetActualType(bsonReader, nominalType); // returns nominalType if no discriminator found } var serializer = memberMap.GetSerializer(actualType); diff --git a/Bson/Serialization/BsonDefaultSerializer.cs b/Bson/Serialization/BsonDefaultSerializer.cs index 51443d6b6dc..fee51278538 100644 --- a/Bson/Serialization/BsonDefaultSerializer.cs +++ b/Bson/Serialization/BsonDefaultSerializer.cs @@ -44,7 +44,6 @@ public class BsonDefaultSerializer : IBsonSerializationProvider private static BsonDefaultSerializer __instance = new BsonDefaultSerializer(); private static Dictionary __serializers; private static Dictionary __genericSerializerDefinitions; - private static Dictionary __discriminatorConventions = new Dictionary(); private static Dictionary> __discriminators = new Dictionary>(); private static HashSet __typesWithRegisteredKnownTypes = new HashSet(); private static HashSet __discriminatedTypes = new HashSet(); @@ -242,71 +241,88 @@ public static Type LookupActualType(Type nominalType, BsonValue discriminator) /// A discriminator convention. public static IDiscriminatorConvention LookupDiscriminatorConvention(Type type) { - BsonSerializer.ConfigLock.EnterReadLock(); - try + FastSingleton singleton; + + if (!TryLookupDiscriminatorConvention(type, out singleton)) { - IDiscriminatorConvention convention; - if (__discriminatorConventions.TryGetValue(type, out convention)) - { - return convention; - } + var message = string.Format("No discriminator convention found for type {0}.", type.FullName); + throw new BsonSerializationException(message); } - finally + + return singleton.Value; + } + + /// + /// Looks up and populates the discriminator convention singleton for a type. + /// + /// The type. + /// The discriminator convention singleton for the type. + /// Whether a discriminator convention was returned for the Type. + private static bool TryLookupDiscriminatorConvention( + Type type, + out FastSingleton singleton) + { + singleton = FastSingleton.Lookup(type); + if (singleton.Value != null) { - BsonSerializer.ConfigLock.ExitReadLock(); + return true; } BsonSerializer.ConfigLock.EnterWriteLock(); try { - IDiscriminatorConvention convention; - if (!__discriminatorConventions.TryGetValue(type, out convention)) + if (singleton.Value != null) { - // if there is no convention registered for object register the default one - if (!__discriminatorConventions.ContainsKey(typeof(object))) - { - var defaultDiscriminatorConvention = StandardDiscriminatorConvention.Hierarchical; - __discriminatorConventions.Add(typeof(object), defaultDiscriminatorConvention); - if (type == typeof(object)) - { - return defaultDiscriminatorConvention; - } - } + return true; + } - if (type.IsInterface) + // if there is no convention registered for object register the default one + var objectSingleton = (FastSingleton)FastSingleton.Instance; + if (objectSingleton.Value == null) + { + var defaultDiscriminatorConvention = StandardDiscriminatorConvention.Hierarchical; + objectSingleton.TrySetValue(defaultDiscriminatorConvention, null); + if (type == typeof(object)) { - // TODO: should convention for interfaces be inherited from parent interfaces? - convention = __discriminatorConventions[typeof(object)]; - __discriminatorConventions[type] = convention; + singleton = objectSingleton; + return true; } - else + } + + if (type.IsInterface) + { + // TODO: should convention for interfaces be inherited from parent interfaces? + singleton = FastSingleton.Lookup(type); + singleton.TrySetValue(objectSingleton.Value, null); + } + else + { + // inherit the discriminator convention from the closest parent that has one + Type parentType = type.BaseType; + for (;;) { - // inherit the discriminator convention from the closest parent that has one - Type parentType = type.BaseType; - while (convention == null) + if (parentType == null) { - if (parentType == null) - { - var message = string.Format("No discriminator convention found for type {0}.", type.FullName); - throw new BsonSerializationException(message); - } - if (__discriminatorConventions.TryGetValue(parentType, out convention)) - { - break; - } - parentType = parentType.BaseType; + singleton = null; + return false; } - - // register this convention for all types between this and the parent type where we found the convention - var unregisteredType = type; - while (unregisteredType != parentType) + singleton = FastSingleton.Lookup(parentType); + if (singleton.Value != null) { - BsonDefaultSerializer.RegisterDiscriminatorConvention(unregisteredType, convention); - unregisteredType = unregisteredType.BaseType; + break; } + parentType = parentType.BaseType; + } + + // register this convention for all types between this and the parent type where we found the convention + var unregisteredType = type; + while (unregisteredType != parentType) + { + BsonDefaultSerializer.RegisterDiscriminatorConvention(unregisteredType, singleton.Value); + unregisteredType = unregisteredType.BaseType; } } - return convention; + return true; } finally { @@ -364,11 +380,8 @@ public static void RegisterDiscriminatorConvention(Type type, IDiscriminatorConv BsonSerializer.ConfigLock.EnterWriteLock(); try { - if (!__discriminatorConventions.ContainsKey(type)) - { - __discriminatorConventions.Add(type, convention); - } - else + var singleton = FastSingleton.Lookup(type); + if (!singleton.TrySetValue(convention, null)) { var message = string.Format("There is already a discriminator convention registered for type {0}.", type.FullName); throw new BsonSerializationException(message); diff --git a/Bson/Serialization/BsonMemberMap.cs b/Bson/Serialization/BsonMemberMap.cs index 3e4c12fc7da..27fd5765933 100755 --- a/Bson/Serialization/BsonMemberMap.cs +++ b/Bson/Serialization/BsonMemberMap.cs @@ -50,6 +50,8 @@ public class BsonMemberMap private bool _ignoreIfDefault; private bool _ignoreIfNull; private object _defaultValue; + private readonly FastSingleton _discriminatorConvention; + private readonly FastSingleton _singletonSerializer; // constructors /// @@ -63,6 +65,8 @@ public BsonMemberMap(BsonClassMap classMap, MemberInfo memberInfo) _memberInfo = memberInfo; _memberType = BsonClassMap.GetMemberInfoType(memberInfo); _defaultValue = GetDefaultValue(_memberType); + _discriminatorConvention = FastSingleton.Lookup(_memberType); + _singletonSerializer = FastSingleton.Lookup(_memberType); } // public properties @@ -289,10 +293,34 @@ public IBsonSerializer GetSerializer(Type actualType) { return _serializer; } - else + + if (actualType != _memberType) { return BsonSerializer.LookupSerializer(actualType); } + + if (_singletonSerializer.Value == null) + { + // LookupSerializer populates _singletonSerializer + BsonSerializer.LookupSerializer(_memberType); + } + + return _singletonSerializer.Value; + } + + /// + /// Gets the discriminator convention for the member type. + /// + /// The discriminator convention for the member type. + internal IDiscriminatorConvention GetDiscriminatorConvention() + { + if (_discriminatorConvention.Value == null) + { + // LookupDiscriminatorConvention will populate _discriminatorConvention + BsonDefaultSerializer.LookupDiscriminatorConvention(_memberType); + } + + return _discriminatorConvention.Value; } /// diff --git a/Bson/Serialization/BsonSerializer.cs b/Bson/Serialization/BsonSerializer.cs index 08393a1e4ff..bd4acb0ac5c 100644 --- a/Bson/Serialization/BsonSerializer.cs +++ b/Bson/Serialization/BsonSerializer.cs @@ -34,7 +34,6 @@ public static class BsonSerializer // private static fields private static ReaderWriterLockSlim __configLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); private static Dictionary __idGenerators = new Dictionary(); - private static Dictionary __serializers = new Dictionary(); private static Dictionary __genericSerializerDefinitions = new Dictionary(); private static List __serializationProviders = new List(); private static bool __useNullIdChecker = false; @@ -43,7 +42,6 @@ public static class BsonSerializer // static constructor static BsonSerializer() { - RegisterDefaultSerializationProvider(); RegisterIdGenerators(); } @@ -359,76 +357,21 @@ public static IIdGenerator LookupIdGenerator(Type type) } /// - /// Looks up a serializer for a Type. + /// Looks up and populates the serializer singleton for a type. /// /// The Type. /// A serializer for the Type. public static IBsonSerializer LookupSerializer(Type type) { - __configLock.EnterReadLock(); - try - { - IBsonSerializer serializer; - if (__serializers.TryGetValue(type, out serializer)) - { - return serializer; - } - } - finally - { - __configLock.ExitReadLock(); - } + FastSingleton singleton; - __configLock.EnterWriteLock(); - try + if (!TryLookupSerializer(type, out singleton)) { - IBsonSerializer serializer; - if (!__serializers.TryGetValue(type, out serializer)) - { - // special case for IBsonSerializable - if (serializer == null && typeof(IBsonSerializable).IsAssignableFrom(type)) - { - serializer = Serializers.BsonIBsonSerializableSerializer.Instance; - } - - if (serializer == null && type.IsGenericType) - { - var genericTypeDefinition = type.GetGenericTypeDefinition(); - var genericSerializerDefinition = LookupGenericSerializerDefinition(genericTypeDefinition); - if (genericSerializerDefinition != null) - { - var genericSerializerType = genericSerializerDefinition.MakeGenericType(type.GetGenericArguments()); - serializer = (IBsonSerializer)Activator.CreateInstance(genericSerializerType); - } - } - - if (serializer == null) - { - foreach (var serializationProvider in __serializationProviders) - { - serializer = serializationProvider.GetSerializer(type); - if (serializer != null) - { - break; - } - } - } - - if (serializer == null) - { - var message = string.Format("No serializer found for type {0}.", type.FullName); - throw new BsonSerializationException(message); - } - - __serializers[type] = serializer; - } - - return serializer; - } - finally - { - __configLock.ExitWriteLock(); + var message = string.Format("No serializer found for type {0}.", type.FullName); + throw new BsonSerializationException(message); } + + return singleton.Value; } /// @@ -475,6 +418,11 @@ public static void RegisterIdGenerator(Type type, IIdGenerator idGenerator) /// The serialization provider. public static void RegisterSerializationProvider(IBsonSerializationProvider provider) { + if (provider == BsonDefaultSerializer.Instance) + { + throw new ArgumentException("BsonDefaultSerializer is implicitly registered", "provider"); + } + __configLock.EnterWriteLock(); try { @@ -497,7 +445,12 @@ public static void RegisterSerializer(Type type, IBsonSerializer serializer) __configLock.EnterWriteLock(); try { - __serializers[type] = serializer; + var singleton = FastSingleton.Lookup(type); + if (!singleton.TrySetValue(serializer, null)) + { + var message = string.Format("There is already a serializer registered for type {0}.", type.FullName); + throw new BsonSerializationException(message); + } } finally { @@ -555,24 +508,93 @@ public static void Serialize( object value, IBsonSerializationOptions options) { - var bsonSerializable = value as IBsonSerializable; - if (bsonSerializable != null) - { - bsonSerializable.Serialize(bsonWriter, nominalType, options); - return; - } - var actualType = (value == null) ? nominalType : value.GetType(); var serializer = LookupSerializer(actualType); serializer.Serialize(bsonWriter, nominalType, value, options); } - // private static methods - private static void RegisterDefaultSerializationProvider() - { - RegisterSerializationProvider(BsonDefaultSerializer.Instance); + /// + /// Looks up and populates the serializer singleton for a type. + /// + /// The Type. + /// The serializer singleton for the type. + /// Whether a serializer was returned for the Type. + internal static bool TryLookupSerializer( + Type type, + out FastSingleton singleton) + { + singleton = FastSingleton.Lookup(type); + if (singleton.Value != null) + { + return true; + } + + __configLock.EnterWriteLock(); + try + { + if (singleton.Value != null) + { + return true; + } + + IBsonSerializer serializer; + + if (__serializationProviders.Count > 0) + { + foreach (var serializationProvider in __serializationProviders) + { + serializer = serializationProvider.GetSerializer(type); + if (serializer != null) + { + singleton.TrySetValue(serializer, null); + return true; + } + } + } + + if (typeof(IBsonSerializable).IsAssignableFrom(type)) + { + serializer = Serializers.BsonIBsonSerializableSerializer.Instance; + if (serializer != null) + { + singleton.TrySetValue(serializer, null); + return true; + } + } + + if (type.IsGenericType) + { + var genericTypeDefinition = type.GetGenericTypeDefinition(); + var genericSerializerDefinition = LookupGenericSerializerDefinition(genericTypeDefinition); + if (genericSerializerDefinition != null) + { + var genericSerializerType = genericSerializerDefinition.MakeGenericType(type.GetGenericArguments()); + serializer = (IBsonSerializer)Activator.CreateInstance(genericSerializerType); + if (serializer != null) + { + singleton.TrySetValue(serializer, null); + return true; + } + } + } + + serializer = BsonDefaultSerializer.Instance.GetSerializer(type); + if (serializer != null) + { + singleton.TrySetValue(serializer, null); + return true; + } + + serializer = null; + return false; + } + finally + { + __configLock.ExitWriteLock(); + } } + // private static methods private static void RegisterIdGenerators() { BsonSerializer.RegisterIdGenerator(typeof(BsonObjectId), BsonObjectIdGenerator.Instance); diff --git a/Bson/Serialization/Serializers/ImageSerializers.cs b/Bson/Serialization/Serializers/ImageSerializers.cs index 8b76f6e2e0e..141f1c56507 100644 --- a/Bson/Serialization/Serializers/ImageSerializers.cs +++ b/Bson/Serialization/Serializers/ImageSerializers.cs @@ -24,6 +24,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Conventions; namespace MongoDB.Bson.Serialization.Serializers { @@ -71,7 +72,12 @@ public override object Deserialize( throw new ArgumentException(message, "nominalType"); } - var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(Image)); + var discriminatorConvention = FastSingleton.Instance.Value; + if (discriminatorConvention == null) + { + // LookupDiscriminatorConvention populates FastSingleton + discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(Image)); + } var actualType = discriminatorConvention.GetActualType(bsonReader, typeof(Image)); if (actualType == typeof(Image)) { diff --git a/Bson/Serialization/Serializers/ObjectSerializer.cs b/Bson/Serialization/Serializers/ObjectSerializer.cs index 382c845117a..cfef8afacd1 100644 --- a/Bson/Serialization/Serializers/ObjectSerializer.cs +++ b/Bson/Serialization/Serializers/ObjectSerializer.cs @@ -22,6 +22,7 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Conventions; namespace MongoDB.Bson.Serialization.Serializers { @@ -87,7 +88,12 @@ public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializ } } - var discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(object)); + var discriminatorConvention = FastSingleton.Instance.Value; + if (discriminatorConvention == null) + { + // LookupDiscriminatorConvention populates FastSingleton + discriminatorConvention = BsonDefaultSerializer.LookupDiscriminatorConvention(typeof(object)); + } var actualType = discriminatorConvention.GetActualType(bsonReader, typeof(object)); if (actualType == typeof(object)) {