SingletonDispenser<TKey, TItem>
using Castle.Core.Internal;
using System;
using System.Collections.Generic;
using System.Threading;
namespace Castle.Components.DictionaryAdapter.Xml
{
public class SingletonDispenser<TKey, TItem> where TItem : class
{
private readonly Lock locker;
private readonly Dictionary<TKey, object> items;
private readonly Func<TKey, TItem> factory;
public TItem this[TKey key] {
get {
return GetOrCreate(key);
}
protected set {
items[key] = value;
}
}
public SingletonDispenser(Func<TKey, TItem> factory)
{
if (factory == null)
throw Error.ArgumentNull("factory");
locker = new SlimReadWriteLock();
items = new Dictionary<TKey, object>();
this.factory = factory;
}
private TItem GetOrCreate(TKey key)
{
if (!TryGetExistingItem(key, out object item))
return Create(key, item);
return (item as TItem) ?? WaitForCreate(key, item);
}
private bool TryGetExistingItem(TKey key, out object item)
{
using (locker.ForReading()) {
if (items.TryGetValue(key, out item))
return true;
}
using (IUpgradeableLockHolder upgradeableLockHolder = locker.ForReadingUpgradeable()) {
if (items.TryGetValue(key, out item))
return true;
using (upgradeableLockHolder.Upgrade())
items[key] = (item = new ManualResetEvent(false));
}
return false;
}
private TItem WaitForCreate(TKey key, object item)
{
ManualResetEvent manualResetEvent = (ManualResetEvent)item;
manualResetEvent.WaitOne();
using (locker.ForReading())
return (TItem)items[key];
}
private TItem Create(TKey key, object item)
{
ManualResetEvent manualResetEvent = (ManualResetEvent)item;
TItem val = factory(key);
using (locker.ForWriting())
items[key] = val;
manualResetEvent.Set();
return val;
}
}
}