Browse Source

Reuse Conversions instance across multiple files by storing it in the CacheManager.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
47eb18363e
  1. 10
      ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs
  2. 19
      ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs
  3. 8
      ICSharpCode.NRefactory/CSharp/Resolver/MemberTypeOrNamespaceReference.cs
  4. 2
      ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs
  5. 8
      ICSharpCode.NRefactory/CSharp/Resolver/SimpleTypeOrNamespaceReference.cs
  6. 2
      ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs
  7. 129
      ICSharpCode.NRefactory/Utils/CacheManager.cs

10
ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs

@ -54,7 +54,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -54,7 +54,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
throw new ArgumentNullException("context");
this.context = context;
this.cancellationToken = cancellationToken;
this.conversions = new Conversions(context);
this.conversions = Conversions.Get(context);
}
#endregion
@ -100,12 +100,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -100,12 +100,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
CacheManager cache = context.CacheManager;
if (cache != null) {
object obj;
if (cache.Dictionary.TryGetValue(value, out obj)) {
currentUsingScope = (UsingScopeCache)obj;
} else {
currentUsingScope = cache.GetShared(value) as UsingScopeCache;
if (currentUsingScope == null) {
currentUsingScope = new UsingScopeCache(value);
cache.Dictionary.TryAdd(value, currentUsingScope);
cache.SetShared(value, currentUsingScope);
}
} else {
currentUsingScope = new UsingScopeCache(value);

19
ICSharpCode.NRefactory/CSharp/Resolver/Conversions.cs

@ -248,6 +248,25 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -248,6 +248,25 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.dynamicErasure = new DynamicErasure(this);
}
/// <summary>
/// Gets the Conversions instance for the specified <see cref="ITypeResolveContext"/>.
/// This will make use of the context's cache manager (if available) to reuse the Conversions instance.
/// </summary>
public static Conversions Get(ITypeResolveContext context)
{
CacheManager cache = context.CacheManager;
if (cache != null) {
Conversions conversions = cache.GetThreadLocal(typeof(Conversions)) as Conversions;
if (conversions == null) {
conversions = new Conversions(context);
cache.SetThreadLocal(typeof(Conversions), conversions);
}
return conversions;
} else {
return new Conversions(context);
}
}
#region TypePair (for caching)
struct TypePair : IEquatable<TypePair>
{

8
ICSharpCode.NRefactory/CSharp/Resolver/MemberTypeOrNamespaceReference.cs

@ -61,9 +61,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -61,9 +61,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
CacheManager cacheManager = context.CacheManager;
if (cacheManager != null) {
object result;
if (cacheManager.Dictionary.TryGetValue(this, out result))
return (ResolveResult)result;
ResolveResult cachedResult = cacheManager.GetShared(this) as ResolveResult;;
if (cachedResult != null)
return cachedResult;
}
ResolveResult targetRR = target.DoResolve(context);
@ -78,7 +78,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -78,7 +78,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
ResolveResult rr = r.ResolveMemberType(targetRR, identifier, typeArgs);
if (cacheManager != null)
cacheManager.Dictionary.TryAdd(this, rr);
cacheManager.SetShared(this, rr);
return rr;
}

2
ICSharpCode.NRefactory/CSharp/Resolver/OverloadResolution.cs

@ -137,7 +137,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -137,7 +137,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (typeArguments != null && typeArguments.Length > 0)
this.explicitlyGivenTypeArguments = typeArguments;
this.conversions = conversions ?? new Conversions(context);
this.conversions = conversions ?? Conversions.Get(context);
this.AllowExpandingParams = true;
}
#endregion

8
ICSharpCode.NRefactory/CSharp/Resolver/SimpleTypeOrNamespaceReference.cs

@ -59,9 +59,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -59,9 +59,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
{
CacheManager cacheManager = context.CacheManager;
if (cacheManager != null) {
object result;
if (cacheManager.Dictionary.TryGetValue(this, out result))
return (ResolveResult)result;
ResolveResult cachedResult = cacheManager.GetShared(this) as ResolveResult;
if (cachedResult != null)
return cachedResult;
}
CSharpResolver r = new CSharpResolver(context);
@ -73,7 +73,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -73,7 +73,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
ResolveResult rr = r.LookupSimpleNameOrTypeName(identifier, typeArgs, lookupMode);
if (cacheManager != null)
cacheManager.Dictionary.TryAdd(this, rr);
cacheManager.SetShared(this, rr);
return rr;
}

2
ICSharpCode.NRefactory/CSharp/Resolver/TypeInference.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -61,7 +61,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
this.conversions = conversions ?? new Conversions(context);
this.conversions = conversions ?? Conversions.Get(context);
}
#endregion

129
ICSharpCode.NRefactory/Utils/CacheManager.cs

@ -18,107 +18,47 @@ @@ -18,107 +18,47 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace ICSharpCode.NRefactory.Utils
{
/// <summary>
/// Allows the registration of static "caching types" which can then be used to efficiently retrieve an
/// instance per CacheManager (or even per CacheManager and thread).
/// Allows caching values for a specific resolve context.
/// A CacheManager consists of two dictionaries: one for shared instances (shared among all threads working with that resolve context),
/// and one for thread-local instances.
/// Additionally, it provides a Dispose() event that can be used to clear any external caches when
/// leaving the "using (var ctx = context.Synchronize())" block.
/// </summary>
/// <remarks>This class is thread-safe</remarks>
public sealed class CacheManager : IDisposable
{
readonly ConcurrentDictionary<object, object> dict = new ConcurrentDictionary<object, object>(ReferenceComparer.Instance);
readonly ConcurrentDictionary<object, object> sharedDict = new ConcurrentDictionary<object, object>(ReferenceComparer.Instance);
readonly ThreadLocal<Dictionary<object, object>> localDict = new ThreadLocal<Dictionary<object, object>>(() => new Dictionary<object, object>(ReferenceComparer.Instance));
public ConcurrentDictionary<object, object> Dictionary {
get { return dict; }
public object GetShared(object key)
{
object val;
sharedDict.TryGetValue(key, out val);
return val;
}
/* Lots of code commented out because I don't know if it's useful, clients can usually replicate
* the functionality much more easily and only need the Disposed event to ensure cleanup.
*
* Actually, what I've implemented here looks very much like .NET's internal System.LocalDataStore
* (used for Thread.GetData/SetData)
*
static int nextSharedIndex, nextThreadLocalIndex;
/// <summary>
/// Registers a new cache type. This causes each CacheManager to allocate space for the new cache type.
/// </summary>
/// <param name="isThreadLocal">Specifies whether this cache is shared (multi-threaded) or whether
/// there is one instance per thread.</param>
/// <returns>Returns a token that can be used to access the cache.</returns>
public static CacheToken<T> RegisterType<T>(CacheMode mode) where T : class, new()
public void SetShared(object key, object val)
{
int index;
switch (mode) {
case CacheMode.Shared:
index = Interlocked.Increment(ref nextSharedIndex);
break;
case CacheMode.ThreadLocal:
index = Interlocked.Increment(ref nextThreadLocalIndex);
break;
default:
throw new ArgumentException("Invalid value for CacheMode", "mode");
}
return new CacheToken<T>(mode, index);
sharedDict[key] = val;
}
readonly object lockObj = new object();
volatile object[] _sharedCaches = new object[nextSharedIndex];
ThreadLocal<object[]> threadLocalCaches = new ThreadLocal<object[]>(() => new object[nextThreadLocalIndex]);
public object GetThreadLocal(object key)
{
object val;
localDict.Value.TryGetValue(key, out val);
return val;
}
/// <summary>
/// Gets the cache using the specified token.
/// </summary>
public T Get<T>(CacheToken<T> token) where T : class, new()
public void SetThreadLocal(object key, object val)
{
switch (token.Mode) {
case CacheMode.Shared:
object[] sharedCaches = this._sharedCaches;
if (token.Index < sharedCaches.Length) {
object c = sharedCaches[token.Index];
if (c != null)
return (T)c;
}
// it seems like the cache doesn't exist yet, so try to create it:
T newCache = new T();
lock (lockObj) {
sharedCaches = this._sharedCaches; // fetch fresh value after locking
// use double-checked locking
if (token.Index < sharedCaches.Length) {
object c = sharedCaches[token.Index];
if (c != null) {
// looks like someone else was faster creating it than this thread
return (T)c;
}
} else {
Array.Resize(ref sharedCaches, nextSharedIndex);
this._sharedCaches = sharedCaches;
}
sharedCaches[token.Index] = newCache;
}
return newCache;
case CacheMode.ThreadLocal:
object[] localCaches = threadLocalCaches.Value;
if (token.Index >= localCaches.Length) {
Array.Resize(ref localCaches, nextThreadLocalIndex);
threadLocalCaches.Value = localCaches;
}
object lc = localCaches[token.Index];
if (lc != null) {
return (T)lc;
} else {
T newLocalCache = new T();
localCaches[token.Index] = newLocalCache;
return newLocalCache;
}
default:
throw new ArgumentException("Invalid token");
}
localDict.Value[key] = val;
}
*/
public event EventHandler Disposed;
@ -127,7 +67,8 @@ namespace ICSharpCode.NRefactory.Utils @@ -127,7 +67,8 @@ namespace ICSharpCode.NRefactory.Utils
/// </summary>
public void Dispose()
{
//threadLocalCaches.Dispose(); // dispose the ThreadLocal<T>
sharedDict.Clear();
localDict.Dispose(); // dispose the ThreadLocal<T>
// TODO: test whether this frees the referenced value on all threads
// fire Disposed() only once by removing the old event handlers
@ -136,24 +77,4 @@ namespace ICSharpCode.NRefactory.Utils @@ -136,24 +77,4 @@ namespace ICSharpCode.NRefactory.Utils
disposed(this, EventArgs.Empty);
}
}
/*
public enum CacheMode
{
// don't use 0 so that default(CacheToken<...>) is an invalid mode
Shared = 1,
ThreadLocal = 2
}
public struct CacheToken<T> where T : class, new()
{
internal readonly CacheMode Mode;
internal readonly int Index;
internal CacheToken(CacheMode mode, int index)
{
this.Mode = mode;
this.Index = index;
}
}*/
}

Loading…
Cancel
Save