SPRNET-1558 HashtableCachingAdvisorChainFactory is not thread safe
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user