Skip to content

Commit

Permalink
Abstract away ReaderWriterLockSlim in most cases
Browse files Browse the repository at this point in the history
`ReaderWriterLockSlim` is used mostly in conjunction with regular
dictionaries. The double-checked lock pattern is used in many places
to query and update those dictionaries.

Instead of repeating this same pattern over and over again, abstract
it away with a dictionary-like type `SynchronizedDictionary<,>` that
performs the required locking pattern.

`ModuleScope`'s internal methods for accessing the type cache can now
be replaced with a simple internal property `TypeCache`.
  • Loading branch information
stakx committed Jun 23, 2018
1 parent 48dbd11 commit 3ebd4ba
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 217 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ namespace Castle.Components.DictionaryAdapter
/// </summary>
public class DictionaryAdapterFactory : IDictionaryAdapterFactory
{
private readonly Dictionary<Type, DictionaryAdapterMeta> interfaceToMeta = new Dictionary<Type, DictionaryAdapterMeta>();

private readonly ReaderWriterLockSlim interfaceToMetaLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private readonly SynchronizedDictionary<Type, DictionaryAdapterMeta> interfaceToMeta =
new SynchronizedDictionary<Type, DictionaryAdapterMeta>();

#region IDictionaryAdapterFactory

Expand Down Expand Up @@ -130,50 +129,21 @@ private DictionaryAdapterMeta InternalGetAdapterMeta(Type type,
if (type.GetTypeInfo().IsInterface == false)
throw new ArgumentException("Only interfaces can be adapted to a dictionary", "type");

DictionaryAdapterMeta meta;

interfaceToMetaLock.EnterReadLock();
try
{
if (interfaceToMeta.TryGetValue(type, out meta))
{
return meta;
}
}
finally
{
interfaceToMetaLock.ExitReadLock();
}

interfaceToMetaLock.EnterWriteLock();
try
return interfaceToMeta.GetOrAdd(type, t =>
{
if (interfaceToMeta.TryGetValue(type, out meta))
if (descriptor == null && other != null)
{
return meta;
descriptor = other.CreateDescriptor();
}
else
{
if (descriptor == null && other != null)
{
descriptor = other.CreateDescriptor();
}
#if FEATURE_LEGACY_REFLECTION_API
var appDomain = Thread.GetDomain();
var typeBuilder = CreateTypeBuilder(type, appDomain);
var appDomain = Thread.GetDomain();
var typeBuilder = CreateTypeBuilder(type, appDomain);
#else
var typeBuilder = CreateTypeBuilder(type);
var typeBuilder = CreateTypeBuilder(type);
#endif
meta = CreateAdapterMeta(type, typeBuilder, descriptor);
interfaceToMeta.Add(type, meta);
return meta;
}
}
finally
{
interfaceToMetaLock.ExitWriteLock();
}
return CreateAdapterMeta(type, typeBuilder, descriptor);
});
}

private object InternalGetAdapter(Type type, IDictionary dictionary, PropertyDescriptor descriptor)
Expand Down
111 changes: 111 additions & 0 deletions src/Castle.Core/Core/Internal/SynchronizedDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2004-2018 Castle Project - http://www.castleproject.org/
//
// 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.

namespace Castle.Core.Internal
{
using System;
using System.Collections.Generic;
using System.Threading;

internal sealed class SynchronizedDictionary<TKey, TValue> : IDisposable
{
private Dictionary<TKey, TValue> items;
private ReaderWriterLockSlim itemsLock;

public SynchronizedDictionary()
{
items = new Dictionary<TKey, TValue>();
itemsLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
}

[Obsolete] // TODO: Remove this property along with the `ModuleScope.Lock` property.
public ReaderWriterLockSlim Lock => itemsLock;

public void AddOrUpdateWithoutTakingLock(TKey key, TValue value)
{
items[key] = value;
}

public void Dispose()
{
itemsLock.Dispose();
}

public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
{
TValue value;

itemsLock.EnterReadLock();
try
{
if (items.TryGetValue(key, out value))
{
return value;
}
}
finally
{
itemsLock.ExitReadLock();
}

itemsLock.EnterWriteLock();
try
{
return GetOrAddWithoutTakingLock(key, valueFactory);
}
finally
{
itemsLock.ExitWriteLock();
}
}

public TValue GetOrAddWithoutTakingLock(TKey key, Func<TKey, TValue> valueFactory)
{
TValue value;

if (items.TryGetValue(key, out value))
{
return value;
}
else
{
value = valueFactory.Invoke(key);
items.Add(key, value);
return value;
}
}

public void ForEach(Action<TKey, TValue> action)
{
itemsLock.EnterReadLock();
try
{
foreach (var item in items)
{
action.Invoke(item.Key, item.Value);
}
}
finally
{
itemsLock.ExitReadLock();
}
}

[Obsolete] // TODO: Remove this method along with the `ModuleScope.GetFromCache` method.
public bool TryGetValueWithoutTakingLock(TKey key, out TValue value)
{
return items.TryGetValue(key, out value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ private Type GetDelegateType(MetaMethod method, ClassEmitter @class, ProxyGenera
ToArray(),
null);

return scope.GetOrAddToCacheWithoutTakingLock(key, _ =>
return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ =>
new DelegateTypeGenerator(method, targetType)
.Generate(@class, options, namingScope)
.BuildType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private Type GetDelegateType(MetaMethod method, ClassEmitter @class, ProxyGenera
ToArray(),
null);

return scope.GetOrAddToCacheWithoutTakingLock(key, _ =>
return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ =>
new DelegateTypeGenerator(method, targetType)
.Generate(@class, options, namingScope)
.BuildType());
Expand All @@ -141,7 +141,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter @class, ProxyGene

// no locking required as we're already within a lock

return scope.GetOrAddToCacheWithoutTakingLock(key, _ => BuildInvocationType(method, @class, options));
return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ => BuildInvocationType(method, @class, options));
}

private MethodGenerator IndirectlyCalledMethodGenerator(MetaMethod method, ClassEmitter proxy,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter emitter, ProxyGen

// no locking required as we're already within a lock

return scope.GetOrAddToCacheWithoutTakingLock(key, _ =>
return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ =>
new CompositionInvocationTypeGenerator(method.Method.DeclaringType,
method,
method.Method,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter @class, ProxyGene

// no locking required as we're already within a lock

return scope.GetOrAddToCacheWithoutTakingLock(key, _ =>
return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ =>
new CompositionInvocationTypeGenerator(method.Method.DeclaringType,
method,
method.Method,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter emitter, ProxyGen

// no locking required as we're already within a lock

return scope.GetOrAddToCacheWithoutTakingLock(key, _ =>
return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ =>
new CompositionInvocationTypeGenerator(method.Method.DeclaringType,
method,
method.Method,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private Type GetInvocationType(MetaMethod method, ClassEmitter emitter, ProxyGen

// no locking required as we're already within a lock

return scope.GetOrAddToCacheWithoutTakingLock(key, _ =>
return scope.TypeCache.GetOrAddWithoutTakingLock(key, _ =>
new CompositionInvocationTypeGenerator(method.Method.DeclaringType,
method,
method.Method,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ protected Type ObtainProxyType(CacheKey cacheKey, Func<string, INamingScope, Typ
{
bool notFoundInTypeCache = false;

var proxyType = Scope.GetOrAddToCache(cacheKey, _ =>
var proxyType = Scope.TypeCache.GetOrAdd(cacheKey, _ =>
{
notFoundInTypeCache = true;
Logger.DebugFormat("No cached proxy type was found for target type {0}.", targetType.FullName);
Expand Down
42 changes: 4 additions & 38 deletions src/Castle.Core/DynamicProxy/Internal/InvocationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@ namespace Castle.DynamicProxy.Internal
using System.Reflection;
using System.Threading;

using Castle.Core.Internal;
using Castle.DynamicProxy.Generators;

public static class InvocationHelper
{
private static readonly Dictionary<CacheKey, MethodInfo> cache =
new Dictionary<CacheKey, MethodInfo>();

private static readonly ReaderWriterLockSlim cacheLock =
new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private static readonly SynchronizedDictionary<CacheKey, MethodInfo> cache =
new SynchronizedDictionary<CacheKey, MethodInfo>();

public static MethodInfo GetMethodOnObject(object target, MethodInfo proxiedMethod)
{
Expand All @@ -52,39 +50,7 @@ public static MethodInfo GetMethodOnType(Type type, MethodInfo proxiedMethod)

var cacheKey = new CacheKey(proxiedMethod, type);

MethodInfo methodOnTarget;

cacheLock.EnterReadLock();
try
{
if (cache.TryGetValue(cacheKey, out methodOnTarget))
{
return methodOnTarget;
}
}
finally
{
cacheLock.ExitReadLock();
}

cacheLock.EnterWriteLock();
try
{
if (cache.TryGetValue(cacheKey, out methodOnTarget))
{
return methodOnTarget;
}
else
{
methodOnTarget = ObtainMethod(proxiedMethod, type);
cache.Add(cacheKey, methodOnTarget);
return methodOnTarget;
}
}
finally
{
cacheLock.ExitWriteLock();
}
return cache.GetOrAdd(cacheKey, ck => ObtainMethod(proxiedMethod, type));
}

private static MethodInfo ObtainMethod(MethodInfo proxiedMethod, Type type)
Expand Down
Loading

0 comments on commit 3ebd4ba

Please sign in to comment.