mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
4.4 KiB
137 lines
4.4 KiB
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) |
|
|
|
using System; |
|
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). |
|
/// </summary> |
|
/// <remarks>This class is thread-safe</remarks> |
|
public sealed class CacheManager : IDisposable |
|
{ |
|
/* 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() |
|
{ |
|
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); |
|
} |
|
|
|
readonly object lockObj = new object(); |
|
volatile object[] _sharedCaches = new object[nextSharedIndex]; |
|
ThreadLocal<object[]> threadLocalCaches = new ThreadLocal<object[]>(() => new object[nextThreadLocalIndex]); |
|
|
|
/// <summary> |
|
/// Gets the cache using the specified token. |
|
/// </summary> |
|
public T Get<T>(CacheToken<T> token) where T : class, new() |
|
{ |
|
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"); |
|
} |
|
} |
|
*/ |
|
|
|
public event EventHandler Disposed; |
|
|
|
/// <summary> |
|
/// Invokes the <see cref="Disposed"/> event. |
|
/// </summary> |
|
public void Dispose() |
|
{ |
|
//threadLocalCaches.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 |
|
EventHandler disposed = Interlocked.Exchange(ref Disposed, null); |
|
if (disposed != null) |
|
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; |
|
} |
|
}*/ |
|
}
|
|
|