Browse Source

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

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

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

@ -248,6 +248,25 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
this.dynamicErasure = new DynamicErasure(this); 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) #region TypePair (for caching)
struct TypePair : IEquatable<TypePair> struct TypePair : IEquatable<TypePair>
{ {

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

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

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

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

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

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

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

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

129
ICSharpCode.NRefactory/Utils/CacheManager.cs

@ -18,107 +18,47 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading; using System.Threading;
namespace ICSharpCode.NRefactory.Utils namespace ICSharpCode.NRefactory.Utils
{ {
/// <summary> /// <summary>
/// Allows the registration of static "caching types" which can then be used to efficiently retrieve an /// Allows caching values for a specific resolve context.
/// instance per CacheManager (or even per CacheManager and thread). /// 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> /// </summary>
/// <remarks>This class is thread-safe</remarks> /// <remarks>This class is thread-safe</remarks>
public sealed class CacheManager : IDisposable 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 { public object GetShared(object key)
get { return dict; } {
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 public void SetShared(object key, object val)
* 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()
{ {
int index; sharedDict[key] = val;
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);
} }
readonly object lockObj = new object(); public object GetThreadLocal(object key)
volatile object[] _sharedCaches = new object[nextSharedIndex]; {
ThreadLocal<object[]> threadLocalCaches = new ThreadLocal<object[]>(() => new object[nextThreadLocalIndex]); object val;
localDict.Value.TryGetValue(key, out val);
return val;
}
/// <summary> public void SetThreadLocal(object key, object val)
/// Gets the cache using the specified token.
/// </summary>
public T Get<T>(CacheToken<T> token) where T : class, new()
{ {
switch (token.Mode) { localDict.Value[key] = val;
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");
}
} }
*/
public event EventHandler Disposed; public event EventHandler Disposed;
@ -127,7 +67,8 @@ namespace ICSharpCode.NRefactory.Utils
/// </summary> /// </summary>
public void Dispose() 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 // TODO: test whether this frees the referenced value on all threads
// fire Disposed() only once by removing the old event handlers // fire Disposed() only once by removing the old event handlers
@ -136,24 +77,4 @@ namespace ICSharpCode.NRefactory.Utils
disposed(this, EventArgs.Empty); 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