Example #1
0
        /// <summary>
        ///		Gets the <see cref="MessagePackSerializer{T}"/> with this instance.
        /// </summary>
        /// <typeparam name="T">Type of serialization/deserialization target.</typeparam>
        /// <param name="providerParameter">A provider specific parameter. See remarks section for details.</param>
        /// <returns>
        ///		<see cref="MessagePackSerializer{T}"/>.
        ///		If there is exiting one, returns it.
        ///		Else the new instance will be created.
        /// </returns>
        /// <remarks>
        ///		<para>
        ///			This method automatically register new instance via <see cref="SerializerRepository.Register{T}(MessagePackSerializer{T})"/>.
        ///		</para>
        ///		<para>
        ///			Currently, only following provider parameters are supported.
        ///			<list type="table">
        ///				<listheader>
        ///					<term>Target type</term>
        ///					<description>Provider parameter</description>
        ///				</listheader>
        ///				<item>
        ///					<term><see cref="EnumMessagePackSerializer{TEnum}"/> or its descendants.</term>
        ///					<description><see cref="EnumSerializationMethod"/>. The returning instance corresponds to this value for serialization.</description>
        ///				</item>
        ///			</list>
        ///			<note><c>null</c> is valid value for <paramref name="providerParameter"/> and it indeicates default behavior of parameter.</note>
        ///		</para>
        /// </remarks>
        public MessagePackSerializer <T> GetSerializer <T>(object providerParameter)
        {
#if !UNITY
            Contract.Ensures(Contract.Result <MessagePackSerializer <T> >() != null);
#endif // !UNITY

            var schema = providerParameter as PolymorphismSchema;
            // Explicitly generated serializer should always used, so get it first.
            MessagePackSerializer <T> serializer = this._serializers.Get <T>(this, providerParameter);

            if (serializer != null)
            {
                return(serializer);
            }

            object aquiredLock = null;
            bool   lockTaken   = false;
            try
            {
                try { }
                finally
                {
                    var newLock = new object();
#if SILVERLIGHT || NETFX_35 || UNITY
                    Monitor.Enter(newLock);
                    try
                    {
                        lock (this._typeLock)
                        {
                            lockTaken = !this._typeLock.TryGetValue(typeof(T), out aquiredLock);
                            if (lockTaken)
                            {
                                aquiredLock = newLock;
                                this._typeLock.Add(typeof(T), newLock);
                            }
                        }
#else
                    bool newLockTaken = false;
                    try
                    {
                        Monitor.Enter(newLock, ref newLockTaken);
                        aquiredLock = this._typeLock.GetOrAdd(typeof(T), _ => newLock);
                        lockTaken   = newLock == aquiredLock;
#endif // if  SILVERLIGHT || NETFX_35 || UNITY
                    }
                    finally
                    {
#if SILVERLIGHT || NETFX_35 || UNITY
                        if (!lockTaken)
#else
                        if (!lockTaken && newLockTaken)
#endif // if SILVERLIGHT || NETFX_35 || UNITY
                        {
                            // Release the lock which failed to become 'primary' lock.
                            Monitor.Exit(newLock);
                        }
                    }
                }

                if (Monitor.TryEnter(aquiredLock))
                {
                    // Decrement monitor counter.
                    Monitor.Exit(aquiredLock);

                    if (lockTaken)
                    {
                        // First try to create generic serializer w/o code generation.
                        serializer = GenericSerializer.Create <T>(this, schema);

                        if (serializer == null)
                        {
#if !XAMIOS && !XAMDROID && !UNITY
                            if (this.IsRuntimeGenerationDisabled)
                            {
#endif // !XAMIOS && !XAMDROID && !UNITY
                            // On debugging, or AOT only envs, use reflection based aproach.
                            serializer =
                                this.GetSerializerWithoutGeneration <T>(schema)
                                ?? MessagePackSerializer.CreateReflectionInternal <T>(this, this.EnsureConcreteTypeRegistered(typeof(T)), schema);
#if !XAMIOS && !XAMDROID && !UNITY
                        }
                        else
                        {
                            // This thread creating new type serializer.
                            serializer = MessagePackSerializer.CreateInternal <T>(this, schema);
                        }
#endif // !XAMIOS && !XAMDROID && !UNITY
                        }
                    }
                    else
                    {
                        // This thread owns existing lock -- thus, constructing self-composite type.

                        // Prevent release owned lock.
                        aquiredLock = null;
                        return(new LazyDelegatingMessagePackSerializer <T>(this, providerParameter));
                    }


                    // Some types always have to use provider.
                    MessagePackSerializerProvider provider;
                    var asEnumSerializer = serializer as ICustomizableEnumSerializer;
                    if (asEnumSerializer != null)
                    {
#if DEBUG && !UNITY
                        Contract.Assert(typeof(T).GetIsEnum(), typeof(T) + " is not enum but generated serializer is ICustomizableEnumSerializer");
#endif // DEBUG && !UNITY

                        provider = new EnumMessagePackSerializerProvider(typeof(T), asEnumSerializer);
                    }
                    else
                    {
#if DEBUG && !UNITY
                        Contract.Assert(!typeof(T).GetIsEnum(), typeof(T) + " is enum but generated serializer is not ICustomizableEnumSerializer : " + (serializer == null ? "null" : serializer.GetType().FullName));
#endif // DEBUG && !UNITY

                        // Creates provider even if no schema -- the schema might be specified future for the type.
                        // It is OK to use polymorphic provider for value type.
#if !UNITY
                        provider = new PolymorphicSerializerProvider <T>(serializer);
#else
                        provider = new PolymorphicSerializerProvider <T>(this, serializer);
#endif // !UNITY
                    }

                    this._serializers.Register(typeof(T), provider);
                }
                else
                {
                    // Wait creation by other thread.
                    // Acquire as 'waiting' lock.
                    Monitor.Enter(aquiredLock);
                }

                // Re-get to avoid duplicated registration and handle provider parameter or get the one created by prececing thread.
                // If T is null and schema is not provided or default schema is provided, then exception will be thrown here from the new provider.
                return(this._serializers.Get <T>(this, providerParameter));
            }
            finally
            {
                if (lockTaken)
                {
#if SILVERLIGHT || NETFX_35 || UNITY
                    lock (this._typeLock)
                    {
                        this._typeLock.Remove(typeof(T));
                    }
#else
                    object dummy;
                    this._typeLock.TryRemove(typeof(T), out dummy);
#endif // if SILVERLIGHT || NETFX_35 || UNITY
                }

                if (aquiredLock != null)
                {
                    // Release primary lock or waiting lock.
                    Monitor.Exit(aquiredLock);
                }
            }
        }
		/// <summary>
		///		Gets the <see cref="MessagePackSerializer{T}"/> with this instance.
		/// </summary>
		/// <typeparam name="T">Type of serialization/deserialization target.</typeparam>
		/// <param name="providerParameter">A provider specific parameter. See remarks section for details.</param>
		/// <returns>
		///		<see cref="MessagePackSerializer{T}"/>.
		///		If there is exiting one, returns it.
		///		Else the new instance will be created.
		/// </returns>
		/// <remarks>
		///		<para>
		///			This method automatically register new instance via <see cref="SerializerRepository.Register{T}(MessagePackSerializer{T})"/>.
		///		</para>
		///		<para>
		///			Currently, only following provider parameters are supported.
		///			<list type="table">
		///				<listheader>
		///					<term>Target type</term>
		///					<description>Provider parameter</description>
		///				</listheader>
		///				<item>
		///					<term><see cref="EnumMessagePackSerializer{TEnum}"/> or its descendants.</term>
		///					<description><see cref="EnumSerializationMethod"/>. The returning instance corresponds to this value for serialization.</description>
		///				</item>
		///			</list>
		///			<note><c>null</c> is valid value for <paramref name="providerParameter"/> and it indeicates default behavior of parameter.</note>
		///		</para>
		/// </remarks>
		public MessagePackSerializer<T> GetSerializer<T>( object providerParameter )
		{
#if DEBUG
			Contract.Ensures( Contract.Result<MessagePackSerializer<T>>() != null );
#endif // DEBUG

			// Explicitly generated serializer should always used, so get it first.
			MessagePackSerializer<T> serializer = this._serializers.Get<T>( this, providerParameter );

			if ( serializer != null )
			{
				return serializer;
			}

			bool lockTaken = false;
			lock ( this._generationLock )
			{
				// Re-get to check because other thread might create the serializer when I wait the lock.
				serializer = this._serializers.Get<T>( this, providerParameter );

				if ( serializer != null )
				{
					return serializer;
				}

				try
				{
					try { }
					finally
					{
#if !FEATURE_CONCURRENT
						lock ( this._typeLock )
						{
							var typeLock = new object();
							object aquiredLock;
							lockTaken = !this._typeLock.TryGetValue( typeof( T ), out aquiredLock );
							if ( lockTaken )
							{
								this._typeLock.Add( typeof( T ), typeLock );
							}
						}
#else
						var typeLock = new object();
						var aquiredTypeLock = this._typeLock.GetOrAdd( typeof( T ), _ => typeLock );
						lockTaken = typeLock == aquiredTypeLock;
#endif // !FEATURE_CONCURRENT
					}

					if ( lockTaken )
					{
						// First try to create generic serializer w/o code generation.
						var schema = ( providerParameter ?? PolymorphismSchema.Create( typeof( T ), null ) ) as PolymorphismSchema;
						serializer = GenericSerializer.Create<T>( this, schema );

						if ( serializer == null )
						{
#if !AOT
							if ( this._serializerGeneratorOptions.DisableRuntimeCodeGeneration )
							{
#endif // AOT
								// On debugging, or AOT only envs, use reflection based aproach.
								serializer =
									this.GetSerializerWithoutGeneration<T>( schema )
									?? this.OnResolveSerializer<T>( schema )
									?? MessagePackSerializer.CreateReflectionInternal<T>( this, this.EnsureConcreteTypeRegistered( typeof( T ) ), schema );
#if !AOT
							}
							else
							{
								// This thread creating new type serializer.
								serializer = this.OnResolveSerializer<T>( schema ) ?? MessagePackSerializer.CreateInternal<T>( this, schema );
							}
#endif // !AOT
						}
					}
					else
					{
						// This thread owns existing lock -- thus, constructing self-composite type.
						return new LazyDelegatingMessagePackSerializer<T>( this, providerParameter );
					}


					// Some types always have to use provider. 
					MessagePackSerializerProvider provider;
					var asEnumSerializer = serializer as ICustomizableEnumSerializer;
					if ( asEnumSerializer != null )
					{
#if DEBUG
						Contract.Assert( typeof( T ).GetIsEnum(), typeof( T ) + " is not enum but generated serializer is ICustomizableEnumSerializer" );
#endif // DEBUG

						provider = new EnumMessagePackSerializerProvider( typeof( T ), asEnumSerializer );
					}
					else
					{
#if DEBUG
						Contract.Assert( !typeof( T ).GetIsEnum(), typeof( T ) + " is enum but generated serializer is not ICustomizableEnumSerializer : " + ( serializer == null ? "null" : serializer.GetType().FullName ) );
#endif // DEBUG

						// Creates provider even if no schema -- the schema might be specified future for the type.
						// It is OK to use polymorphic provider for value type.
#if !UNITY
						provider = new PolymorphicSerializerProvider<T>( serializer );
#else
						provider = new PolymorphicSerializerProvider<T>( this, serializer );
#endif // !UNITY
					}

#if !UNITY
					Type nullableType;
					MessagePackSerializerProvider nullableSerializerProvider;
					SerializerRepository.GetNullableCompanion(
						typeof( T ),
						this,
						serializer,
						out nullableType,
						out nullableSerializerProvider
					);

					this._serializers.Register(
						typeof( T ),
						provider,
						nullableType,
						nullableSerializerProvider,
						SerializerRegistrationOptions.WithNullable
					);
#else
					this._serializers.Register(
						typeof( T ),
						provider,
						null,
						null,
						SerializerRegistrationOptions.None
					);
#endif // !UNITY

					// Re-get to avoid duplicated registration and handle provider parameter or get the one created by prececing thread.
					// If T is null and schema is not provided or default schema is provided, then exception will be thrown here from the new provider.
					return this._serializers.Get<T>( this, providerParameter );
				}
				finally
				{
					if ( lockTaken )
					{
#if !FEATURE_CONCURRENT
					lock ( this._typeLock )
					{
						this._typeLock.Remove( typeof( T ) );
					}
#else
						object dummy;
						this._typeLock.TryRemove( typeof( T ), out dummy );
#endif // !FEATURE_CONCURRENT
					}
				}
			}
		}