diff --git a/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs b/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs index 6824c7cb..a516161f 100644 --- a/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs +++ b/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs @@ -1,7 +1,7 @@ -#region License +#region License /* - * Copyright © 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> methodCache = new Dictionary>(); + // 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> methodCache = new ConcurrentDictionary>(); +#endif + + /// + /// Default c'tor + /// + public HashtableCachingAdvisorChainFactory() + { +#if !NET_4_0 + CreateCacheLock(); +#endif + } + /// /// Gets the list of and /// @@ -59,14 +96,47 @@ namespace Spring.Aop.Framework /// public IList GetInterceptors(IAdvised advised, object proxy, MethodInfo method, Type targetType) { +#if !NET_4_0 IList 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 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 } /// @@ -87,7 +157,19 @@ namespace Spring.Aop.Framework /// public void AdviceChanged(AdvisedSupport source) { +#if !NET_4_0 + cacheLock.EnterWriteLock(); + try + { +#endif methodCache.Clear(); +#if !NET_4_0 + } + finally + { + cacheLock.ExitWriteLock(); + } +#endif } ///