SPRNET-1558 HashtableCachingAdvisorChainFactory is not thread safe

This commit is contained in:
Marko Lahma
2014-02-25 09:31:48 +02:00
parent 5e594b90eb
commit b7e5fa9137

View File

@@ -1,7 +1,7 @@
#region License
#region License
/*
* Copyright <EFBFBD> 2002-2011 the original author or authors.
* Copyright © 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,13 @@ using System;
using System.Collections.Generic;
using System.Reflection;
#if NET_4_0
using System.Collections.Concurrent;
#else
using System.Runtime.Serialization;
using System.Threading;
#endif
#endregion
namespace Spring.Aop.Framework
@@ -37,8 +44,38 @@ namespace Spring.Aop.Framework
[Serializable]
public sealed class HashtableCachingAdvisorChainFactory : IAdvisorChainFactory
{
#if !NET_4_0
private readonly IDictionary<MethodInfo, IList<object>> methodCache = new Dictionary<MethodInfo, IList<object>>();
// ReaderWriterLockSlim is not serializable. Cannot set value using field initializer as it won't
// run on deserialization. Instead c'tor and OnDeserialized will take care of creating the lock instance.
[NonSerialized]
private ReaderWriterLockSlim cacheLock;
[OnDeserialized]
private void OnDeserialized(StreamingContext c)
{
CreateCacheLock();
}
private void CreateCacheLock()
{
cacheLock = new ReaderWriterLockSlim();
}
#else
private readonly ConcurrentDictionary<MethodInfo, IList<object>> methodCache = new ConcurrentDictionary<MethodInfo, IList<object>>();
#endif
/// <summary>
/// Default c'tor
/// </summary>
public HashtableCachingAdvisorChainFactory()
{
#if !NET_4_0
CreateCacheLock();
#endif
}
/// <summary>
/// Gets the list of <see cref="AopAlliance.Intercept.IInterceptor"/> and
/// <see cref="Spring.Aop.Framework.InterceptorAndDynamicMethodMatcher"/>
@@ -59,14 +96,47 @@ namespace Spring.Aop.Framework
/// </returns>
public IList<object> GetInterceptors(IAdvised advised, object proxy, MethodInfo method, Type targetType)
{
#if !NET_4_0
IList<object> cached;
if (!this.methodCache.TryGetValue(method, out cached))
cacheLock.EnterReadLock();
try {
if (this.methodCache.TryGetValue(method, out cached))
{
return cached;
}
}
finally
{
// recalculate...
cached = AdvisorChainFactoryUtils.CalculateInterceptors(advised, proxy, method, targetType);
this.methodCache[method] = cached;
cacheLock.ExitReadLock();
}
// Apparently not in the cache - calculate the value outside of any locks then enter upgradeable read lock and check again
IList<object> calculated = AdvisorChainFactoryUtils.CalculateInterceptors(advised, proxy, method, targetType);
cacheLock.EnterUpgradeableReadLock();
try
{
if (!this.methodCache.TryGetValue(method, out cached))
{
// Still not in the cache - enter write lock and add the pre-calculated value
cacheLock.EnterWriteLock();
try
{
cached = calculated;
this.methodCache[method] = cached;
}
finally
{
cacheLock.ExitWriteLock();
}
}
}
finally
{
cacheLock.ExitUpgradeableReadLock();
}
return cached;
#else
return methodCache.GetOrAdd(method, m => AdvisorChainFactoryUtils.CalculateInterceptors(advised, proxy, m, targetType));
#endif
}
/// <summary>
@@ -87,7 +157,19 @@ namespace Spring.Aop.Framework
/// </param>
public void AdviceChanged(AdvisedSupport source)
{
#if !NET_4_0
cacheLock.EnterWriteLock();
try
{
#endif
methodCache.Clear();
#if !NET_4_0
}
finally
{
cacheLock.ExitWriteLock();
}
#endif
}
/// <summary>